Skip to content

Commit f1efd1d

Browse files
committed
Parsing and rudimentary checking of tuples with rest elements
1 parent 09f17bc commit f1efd1d

8 files changed

Lines changed: 107 additions & 21 deletions

File tree

src/compiler/binder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3602,6 +3602,7 @@ namespace ts {
36023602
case SyntaxKind.ArrayType:
36033603
case SyntaxKind.TupleType:
36043604
case SyntaxKind.OptionalType:
3605+
case SyntaxKind.RestType:
36053606
case SyntaxKind.UnionType:
36063607
case SyntaxKind.IntersectionType:
36073608
case SyntaxKind.ConditionalType:

src/compiler/checker.ts

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3400,9 +3400,12 @@ namespace ts {
34003400
if (typeArguments.length > 0) {
34013401
const arity = getTypeReferenceArity(type);
34023402
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, arity), context);
3403+
const hasRestElement = (<TupleType>type.target).hasRestElement;
34033404
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
34043405
for (let i = (<TupleType>type.target).minLength; i < arity; i++) {
3405-
tupleConstituentNodes[i] = createOptionalTypeNode(tupleConstituentNodes[i]);
3406+
tupleConstituentNodes[i] = hasRestElement && i === arity - 1 ?
3407+
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
3408+
createOptionalTypeNode(tupleConstituentNodes[i]);
34063409
}
34073410
return createTupleTypeNode(tupleConstituentNodes);
34083411
}
@@ -4842,7 +4845,7 @@ namespace ts {
48424845
}
48434846
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
48444847
const elementTypes = map(elements, e => isOmittedExpression(e) ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
4845-
let result = createTupleType(elementTypes);
4848+
let result = <TypeReference>createTupleType(elementTypes);
48464849
if (includePatternInType) {
48474850
result = cloneTypeReference(result);
48484851
result.pattern = pattern;
@@ -8282,21 +8285,25 @@ namespace ts {
82828285
//
82838286
// Note that the generic type created by this function has no symbol associated with it. The same
82848287
// is true for each of the synthesized type parameters.
8285-
function createTupleTypeOfArity(arity: number, minLength: number, associatedNames: __String[] | undefined): TupleType {
8288+
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames: __String[] | undefined): TupleType {
82868289
let typeParameters: TypeParameter[] | undefined;
82878290
const properties: Symbol[] = [];
8291+
const maxLength = hasRestElement ? arity - 1 : arity;
82888292
if (arity) {
82898293
typeParameters = new Array(arity);
82908294
for (let i = 0; i < arity; i++) {
8291-
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String);
8292-
property.type = typeParameters[i] = <TypeParameter>createType(TypeFlags.TypeParameter);
8293-
properties.push(property);
8295+
const typeParameter = typeParameters[i] = <TypeParameter>createType(TypeFlags.TypeParameter);
8296+
if (i < maxLength) {
8297+
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String);
8298+
property.type = typeParameter;
8299+
properties.push(property);
8300+
}
82948301
}
82958302
}
82968303
const literalTypes = [];
8297-
for (let i = minLength; i <= arity; i++) literalTypes.push(getLiteralType(i));
8304+
for (let i = minLength; i <= maxLength; i++) literalTypes.push(getLiteralType(i));
82988305
const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String);
8299-
lengthSymbol.type = getUnionType(literalTypes);
8306+
lengthSymbol.type = hasRestElement ? numberType : getUnionType(literalTypes);
83008307
properties.push(lengthSymbol);
83018308
const type = <TupleType & InterfaceTypeWithDeclaredMembers>createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference);
83028309
type.typeParameters = typeParameters;
@@ -8315,29 +8322,40 @@ namespace ts {
83158322
type.declaredStringIndexInfo = undefined;
83168323
type.declaredNumberIndexInfo = undefined;
83178324
type.minLength = minLength;
8325+
type.hasRestElement = hasRestElement;
83188326
type.associatedNames = associatedNames;
83198327
return type;
83208328
}
83218329

8322-
function getTupleTypeOfArity(arity: number, minLength: number, associatedNames?: __String[]): GenericType {
8323-
const key = arity + "," + minLength + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
8330+
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, associatedNames?: __String[]): GenericType {
8331+
const key = arity + (hasRestElement ? "+" : ",") + minLength + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
83248332
let type = tupleTypes.get(key);
83258333
if (!type) {
8326-
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, associatedNames));
8334+
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, associatedNames));
83278335
}
83288336
return type;
83298337
}
83308338

8331-
function createTupleType(elementTypes: Type[], minLength = elementTypes.length, associatedNames?: __String[]) {
8332-
const tupleType = getTupleTypeOfArity(elementTypes.length, minLength, associatedNames);
8339+
function createTupleType(elementTypes: Type[], minLength = elementTypes.length, hasRestElement = false, associatedNames?: __String[]) {
8340+
const arity = elementTypes.length;
8341+
if (arity === 1 && hasRestElement) {
8342+
return createArrayType(elementTypes[0]);
8343+
}
8344+
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, associatedNames);
83338345
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
83348346
}
83358347

83368348
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
83378349
const links = getNodeLinks(node);
83388350
if (!links.resolvedType) {
8339-
const minLength = findLastIndex(node.elementTypes, n => n.kind !== SyntaxKind.OptionalType) + 1;
8340-
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode), minLength);
8351+
const lastElement = lastOrUndefined(node.elementTypes);
8352+
const restElement = lastElement && lastElement.kind === SyntaxKind.RestType ? lastElement : undefined;
8353+
const minLength = findLastIndex(node.elementTypes, n => n.kind !== SyntaxKind.OptionalType && n !== restElement) + 1;
8354+
const elementTypes = map(node.elementTypes, n => {
8355+
const type = getTypeFromTypeNode(n);
8356+
return n === restElement ? getIndexTypeOfType(type, IndexKind.Number) || errorType : type;
8357+
});
8358+
links.resolvedType = createTupleType(elementTypes, minLength, !!restElement);
83418359
}
83428360
return links.resolvedType;
83438361
}
@@ -9548,9 +9566,10 @@ namespace ts {
95489566
case SyntaxKind.JSDocOptionalType:
95499567
return addOptionality(getTypeFromTypeNode((node as JSDocOptionalType).type));
95509568
case SyntaxKind.ParenthesizedType:
9569+
case SyntaxKind.RestType:
95519570
case SyntaxKind.JSDocNonNullableType:
95529571
case SyntaxKind.JSDocTypeExpression:
9553-
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
9572+
return getTypeFromTypeNode((<ParenthesizedTypeNode | RestTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
95549573
case SyntaxKind.JSDocVariadicType:
95559574
return getTypeFromJSDocVariadicType(node as JSDocVariadicType);
95569575
case SyntaxKind.FunctionType:
@@ -11346,6 +11365,35 @@ namespace ts {
1134611365
}
1134711366
}
1134811367
}
11368+
if (isTupleType(target)) {
11369+
const targetRestType = getRestTypeOfTupleType(<TypeReference>target);
11370+
if (targetRestType) {
11371+
if (!isTupleType(source)) {
11372+
return Ternary.False;
11373+
}
11374+
const sourceRestType = getRestTypeOfTupleType(<TypeReference>source);
11375+
if (sourceRestType && !isRelatedTo(sourceRestType, targetRestType, reportErrors)) {
11376+
if (reportErrors) {
11377+
// !!! Rest element types are incompatible
11378+
reportError(Diagnostics.Index_signatures_are_incompatible);
11379+
}
11380+
return Ternary.False;
11381+
}
11382+
const targetCount = getTypeReferenceArity(<TypeReference>target) - 1;
11383+
const sourceCount = getTypeReferenceArity(<TypeReference>source) - (sourceRestType ? 1 : 0);
11384+
for (let i = targetCount; i < sourceCount; i++) {
11385+
const related = isRelatedTo((<TypeReference>source).typeArguments![i], targetRestType, reportErrors);
11386+
if (!related) {
11387+
if (reportErrors) {
11388+
// !!! Property {0} is incompatible with rest element type
11389+
reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, "" + i);
11390+
}
11391+
return Ternary.False;
11392+
}
11393+
result &= related;
11394+
}
11395+
}
11396+
}
1134911397
return result;
1135011398
}
1135111399

@@ -12028,6 +12076,10 @@ namespace ts {
1202812076
return !!(getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).target.objectFlags & ObjectFlags.Tuple);
1202912077
}
1203012078

12079+
function getRestTypeOfTupleType(type: TypeReference) {
12080+
return (<TupleType>type.target).hasRestElement ? type.typeArguments![type.target.typeParameters!.length - 1] : undefined;
12081+
}
12082+
1203112083
function getFalsyFlagsOfTypes(types: Type[]): TypeFlags {
1203212084
let result: TypeFlags = 0;
1203312085
for (const t of types) {
@@ -12400,7 +12452,7 @@ namespace ts {
1240012452
}
1240112453
const minArgumentCount = getMinArgumentCount(source);
1240212454
const minLength = minArgumentCount < paramCount ? 0 : minArgumentCount - paramCount;
12403-
const rest = sourceHasRest ? createArrayType(getUnionType(types)) : createTupleType(types, minLength, names);
12455+
const rest = sourceHasRest ? createArrayType(getUnionType(types)) : createTupleType(types, minLength, /*hasRestElement*/ false, names);
1240412456
callback(rest, targetRestTypeVariable);
1240512457
}
1240612458
}
@@ -15899,7 +15951,7 @@ namespace ts {
1589915951
// If array literal is actually a destructuring pattern, mark it as an implied type. We do this such
1590015952
// that we get the same behavior for "var [x, y] = []" and "[x, y] = []".
1590115953
if (inDestructuringPattern && elementTypes.length) {
15902-
const type = cloneTypeReference(createTupleType(elementTypes));
15954+
const type = cloneTypeReference(<TypeReference>createTupleType(elementTypes));
1590315955
type.pattern = node;
1590415956
return type;
1590515957
}
@@ -25639,6 +25691,7 @@ namespace ts {
2563925691
return checkUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
2564025692
case SyntaxKind.ParenthesizedType:
2564125693
case SyntaxKind.OptionalType:
25694+
case SyntaxKind.RestType:
2564225695
return checkSourceElement((<ParenthesizedTypeNode | OptionalTypeNode>node).type);
2564325696
case SyntaxKind.TypeOperator:
2564425697
return checkTypeOperator(<TypeOperatorNode>node);

src/compiler/emitter.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,9 @@ namespace ts {
678678
return emitJSDocNonNullableType(node as JSDocNonNullableType);
679679
case SyntaxKind.JSDocOptionalType:
680680
return emitJSDocOptionalType(node as JSDocOptionalType);
681+
case SyntaxKind.RestType:
681682
case SyntaxKind.JSDocVariadicType:
682-
return emitJSDocVariadicType(node as JSDocVariadicType);
683+
return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType);
683684

684685
// Binding patterns
685686
case SyntaxKind.ObjectBindingPattern:
@@ -1287,7 +1288,7 @@ namespace ts {
12871288
writePunctuation("]");
12881289
}
12891290

1290-
function emitJSDocVariadicType(node: JSDocVariadicType) {
1291+
function emitRestOrJSDocVariadicType(node: RestTypeNode | JSDocVariadicType) {
12911292
write("...");
12921293
emit(node.type);
12931294
}

src/compiler/factory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,18 @@ namespace ts {
763763
: node;
764764
}
765765

766+
export function createRestTypeNode(type: TypeNode) {
767+
const node = createSynthesizedNode(SyntaxKind.RestType) as RestTypeNode;
768+
node.type = type;
769+
return node;
770+
}
771+
772+
export function updateRestTypeNode(node: RestTypeNode, type: TypeNode): RestTypeNode {
773+
return node.type !== type
774+
? updateNode(createRestTypeNode(type), node)
775+
: node;
776+
}
777+
766778
export function createUnionTypeNode(types: ReadonlyArray<TypeNode>): UnionTypeNode {
767779
return <UnionTypeNode>createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, types);
768780
}

src/compiler/transformers/ts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ namespace ts {
381381
case SyntaxKind.ArrayType:
382382
case SyntaxKind.TupleType:
383383
case SyntaxKind.OptionalType:
384+
case SyntaxKind.RestType:
384385
case SyntaxKind.TypeLiteral:
385386
case SyntaxKind.TypePredicate:
386387
case SyntaxKind.TypeParameter:

src/compiler/visitor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,10 @@ namespace ts {
379379
return updateOptionalTypeNode((<OptionalTypeNode>node),
380380
visitNode((<OptionalTypeNode>node).type, visitor, isTypeNode));
381381

382+
case SyntaxKind.RestType:
383+
return updateRestTypeNode((<RestTypeNode>node),
384+
visitNode((<RestTypeNode>node).type, visitor, isTypeNode));
385+
382386
case SyntaxKind.UnionType:
383387
return updateUnionTypeNode(<UnionTypeNode>node,
384388
nodesVisitor((<UnionTypeNode>node).types, visitor, isTypeNode));

src/parser/parser.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,12 +446,13 @@ namespace ts {
446446
return visitNode(cbNode, (<JsxClosingElement>node).tagName);
447447

448448
case SyntaxKind.OptionalType:
449+
case SyntaxKind.RestType:
449450
case SyntaxKind.JSDocTypeExpression:
450451
case SyntaxKind.JSDocNonNullableType:
451452
case SyntaxKind.JSDocNullableType:
452453
case SyntaxKind.JSDocOptionalType:
453454
case SyntaxKind.JSDocVariadicType:
454-
return visitNode(cbNode, (<OptionalTypeNode | JSDocTypeExpression | JSDocTypeReferencingNode>node).type);
455+
return visitNode(cbNode, (<OptionalTypeNode | RestTypeNode | JSDocTypeExpression | JSDocTypeReferencingNode>node).type);
455456
case SyntaxKind.JSDocFunctionType:
456457
return visitNodes(cbNode, cbNodes, (<JSDocFunctionType>node).parameters) ||
457458
visitNode(cbNode, (<JSDocFunctionType>node).type);
@@ -2775,6 +2776,12 @@ namespace ts {
27752776
}
27762777

27772778
function parseTupleElementType() {
2779+
const pos = getNodePos();
2780+
if (parseOptional(SyntaxKind.DotDotDotToken)) {
2781+
const node = <RestTypeNode>createNode(SyntaxKind.RestType, pos);
2782+
node.type = parseType();
2783+
return finishNode(node);
2784+
}
27782785
const type = parseType();
27792786
if (!(contextFlags & NodeFlags.JSDoc) && type.kind === SyntaxKind.JSDocNullableType && type.pos === (<JSDocNullableType>type).type.pos) {
27802787
type.kind = SyntaxKind.OptionalType;

src/parser/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ namespace ts {
226226
ArrayType,
227227
TupleType,
228228
OptionalType,
229+
RestType,
229230
UnionType,
230231
IntersectionType,
231232
ConditionalType,
@@ -1107,6 +1108,11 @@ namespace ts {
11071108
type: TypeNode;
11081109
}
11091110

1111+
export interface RestTypeNode extends TypeNode {
1112+
kind: SyntaxKind.RestType;
1113+
type: TypeNode;
1114+
}
1115+
11101116
export type UnionOrIntersectionTypeNode = UnionTypeNode | IntersectionTypeNode;
11111117

11121118
export interface UnionTypeNode extends TypeNode {
@@ -3854,6 +3860,7 @@ namespace ts {
38543860

38553861
export interface TupleType extends GenericType {
38563862
minLength: number;
3863+
hasRestElement: boolean;
38573864
associatedNames?: __String[];
38583865
}
38593866

0 commit comments

Comments
 (0)