Skip to content

Commit c274d9b

Browse files
merge in other PR for type spread node
2 parents 49eec37 + 568b899 commit c274d9b

13 files changed

+211
-4
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3358,6 +3358,7 @@ namespace ts {
33583358
case SyntaxKind.TypeLiteral:
33593359
case SyntaxKind.ArrayType:
33603360
case SyntaxKind.TupleType:
3361+
case SyntaxKind.TypeSpread:
33613362
case SyntaxKind.UnionType:
33623363
case SyntaxKind.IntersectionType:
33633364
case SyntaxKind.ParenthesizedType:

src/compiler/checker.ts

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/// <reference path="moduleNameResolver.ts"/>
22
/// <reference path="binder.ts"/>
33
/// <reference path="symbolWalker.ts" />
4+
/// <reference types="node" />
5+
6+
declare var console: Console;
47

58
/* @internal */
69
namespace ts {
@@ -239,6 +242,7 @@ namespace ts {
239242
const intersectionTypes = createMap<IntersectionType>();
240243
const literalTypes = createMap<LiteralType>();
241244
const indexedAccessTypes = createMap<IndexedAccessType>();
245+
const spreadTypes = createMap<TypeSpreadType>();
242246
const evolvingArrayTypes: EvolvingArrayType[] = [];
243247

244248
const unknownSymbol = createSymbol(SymbolFlags.Property, "unknown" as __String);
@@ -2542,6 +2546,10 @@ namespace ts {
25422546
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
25432547
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
25442548
}
2549+
if (type.flags & TypeFlags.TypeSpread) {
2550+
const typeNode = typeToTypeNodeHelper((<TypeSpreadType>type).type, context);
2551+
return createTypeSpread(typeNode);
2552+
}
25452553

25462554
Debug.fail("Should be unreachable.");
25472555

@@ -3305,6 +3313,10 @@ namespace ts {
33053313
writeType((<IndexedAccessType>type).indexType, TypeFormatFlags.None);
33063314
writePunctuation(writer, SyntaxKind.CloseBracketToken);
33073315
}
3316+
else if (type.flags & TypeFlags.TypeSpread) {
3317+
writePunctuation(writer, SyntaxKind.DotDotDotToken);
3318+
writeType((<TypeSpreadType>type).type, TypeFormatFlags.None);
3319+
}
33083320
else {
33093321
// Should never get here
33103322
// { ... }
@@ -5392,6 +5404,7 @@ namespace ts {
53925404
}
53935405

53945406
function resolveObjectTypeMembers(type: ObjectType, source: InterfaceTypeWithDeclaredMembers, typeParameters: TypeParameter[], typeArguments: Type[]) {
5407+
if (allowSyntheticDefaultImports) console.log("resolveObjectTypeMembers", typeToString(type));
53955408
let mapper: TypeMapper;
53965409
let members: SymbolTable;
53975410
let callSignatures: Signature[];
@@ -7218,11 +7231,69 @@ namespace ts {
72187231
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
72197232
const links = getNodeLinks(node);
72207233
if (!links.resolvedType) {
7221-
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode));
7234+
links.resolvedType = createTupleType(flatMap(node.elementTypes, getTypeFromTupleElement));
72227235
}
72237236
return links.resolvedType;
72247237
}
72257238

7239+
function getTypeSpreadTypes(tuple: Type): Type[] {
7240+
if (isGenericTupleType(tuple)) {
7241+
// Defer the operation by creating a spread type.
7242+
const id = "" + tuple.id;
7243+
let type = spreadTypes.get(id);
7244+
if (!type) {
7245+
spreadTypes.set(id, type = createTypeSpreadType(tuple));
7246+
}
7247+
return [type];
7248+
}
7249+
else {
7250+
// const type = getApparentType(nodeType);
7251+
if (allowSyntheticDefaultImports) {
7252+
console.log("type", typeToString(tuple));
7253+
console.log("isTupleLikeType(type)", isTupleLikeType(tuple));
7254+
}
7255+
if (isTupleLikeType(tuple)) {
7256+
// return map(getPropertiesOfType(tuple), getTypeOfSymbol);
7257+
return getTupleTypeElementTypes(tuple);
7258+
}
7259+
else {
7260+
// error(typeNode, Diagnostics.Tuple_type_spreads_may_only_be_created_from_tuple_types);
7261+
console.log("not a tuple, don't resolve?");
7262+
return [];
7263+
}
7264+
}
7265+
}
7266+
7267+
function isGenericTupleType(type: Type): boolean {
7268+
return type.flags & TypeFlags.TypeVariable ? true :
7269+
type.flags & TypeFlags.UnionOrIntersection ? forEach((<UnionOrIntersectionType>type).types, isGenericTupleType) :
7270+
false;
7271+
}
7272+
7273+
function getTupleTypeElementTypes(type: Type): Type[] {
7274+
Debug.assert(isTupleLikeType(type));
7275+
const types = [];
7276+
let idx = 0;
7277+
let symbol: Symbol;
7278+
while (symbol = getPropertyOfObjectType(type, idx++ + "" as __String)) {
7279+
types.push(getTypeOfSymbol(symbol));
7280+
}
7281+
return types;
7282+
}
7283+
7284+
function getTypeFromTupleElement(node: TypeNode | TypeSpreadTypeNode): Type | Type[] {
7285+
if (node.kind === SyntaxKind.TypeSpread) {
7286+
const links = getNodeLinks(node);
7287+
if (!links.resolvedType) {
7288+
links.resolvedType = getTypeFromTypeNode((node as TypeSpreadTypeNode).type);
7289+
}
7290+
return getTypeSpreadTypes(links.resolvedType);
7291+
}
7292+
else {
7293+
return getTypeFromTypeNode(node as TypeNode);
7294+
}
7295+
}
7296+
72267297
interface TypeSet extends Array<Type> {
72277298
containsAny?: boolean;
72287299
containsUndefined?: boolean;
@@ -7572,6 +7643,13 @@ namespace ts {
75727643
return type;
75737644
}
75747645

7646+
function createTypeSpreadType(tuple: Type) {
7647+
console.log("createTypeSpreadType");
7648+
const type = <TypeSpreadType>createType(TypeFlags.TypeSpread);
7649+
type.type = tuple;
7650+
return type;
7651+
}
7652+
75757653
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode, cacheSymbol: boolean) {
75767654
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
75777655
const propName = indexType.flags & TypeFlags.StringOrNumberLiteral ?
@@ -8389,6 +8467,9 @@ namespace ts {
83898467
if (type.flags & TypeFlags.IndexedAccess) {
83908468
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
83918469
}
8470+
// if (type.flags & TypeFlags.TypeSpread) {
8471+
// return getTypeSpreadTypes(instantiateType((<TypeSpreadType>type).type, mapper));
8472+
// }
83928473
return type;
83938474
}
83948475

@@ -18849,6 +18930,22 @@ namespace ts {
1884918930
forEach(node.elementTypes, checkSourceElement);
1885018931
}
1885118932

18933+
function checkTypeSpreadTypeNode(node: TypeSpreadTypeNode) {
18934+
checkSourceElement(node.type);
18935+
// checkTypeSpreadType(<TypeSpreadType> getTypeFromTypeNode(node.type), node);
18936+
const type = getApparentType(getTypeFromTypeNode(node.type));
18937+
if (!isArrayLikeType(type)) { // isTupleLikeType
18938+
grammarErrorOnNode(node, Diagnostics.Tuple_type_spreads_may_only_be_created_from_tuple_types);
18939+
}
18940+
}
18941+
18942+
// function checkTypeSpreadType(spread: TypeSpreadType, node: TypeSpreadTypeNode) {
18943+
// const type = getApparentType(spread.type);
18944+
// if (!isArrayLikeType(type)) { // isTupleLikeType
18945+
// grammarErrorOnNode(node, Diagnostics.Tuple_type_spreads_may_only_be_created_from_tuple_types);
18946+
// }
18947+
// }
18948+
1885218949
function checkUnionOrIntersectionType(node: UnionOrIntersectionTypeNode) {
1885318950
forEach(node.types, checkSourceElement);
1885418951
}
@@ -22384,6 +22481,8 @@ namespace ts {
2238422481
return checkArrayType(<ArrayTypeNode>node);
2238522482
case SyntaxKind.TupleType:
2238622483
return checkTupleType(<TupleTypeNode>node);
22484+
case SyntaxKind.TypeSpread:
22485+
return checkTypeSpreadTypeNode(<TypeSpreadTypeNode>node);
2238722486
case SyntaxKind.UnionType:
2238822487
case SyntaxKind.IntersectionType:
2238922488
return checkUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2208,6 +2208,10 @@
22082208
"category": "Error",
22092209
"code": 2713
22102210
},
2211+
"Tuple type spreads may only be created from tuple types.": {
2212+
"category": "Error",
2213+
"code": 2714
2214+
},
22112215

22122216
"Import declaration '{0}' is using private name '{1}'.": {
22132217
"category": "Error",

src/compiler/emitter.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,8 @@ namespace ts {
697697
return emitShorthandPropertyAssignment(<ShorthandPropertyAssignment>node);
698698
case SyntaxKind.SpreadAssignment:
699699
return emitSpreadAssignment(node as SpreadAssignment);
700+
case SyntaxKind.TypeSpread:
701+
return emitTypeSpread(node as TypeSpreadTypeNode);
700702

701703
// Enum
702704
case SyntaxKind.EnumMember:
@@ -2201,6 +2203,13 @@ namespace ts {
22012203
}
22022204
}
22032205

2206+
function emitTypeSpread(node: TypeSpreadTypeNode) {
2207+
if (node.type) {
2208+
write("...");
2209+
emit(node.type);
2210+
}
2211+
}
2212+
22042213
//
22052214
// Enum
22062215
//

src/compiler/factory.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,6 +2201,19 @@ namespace ts {
22012201
: node;
22022202
}
22032203

2204+
export function createTypeSpread(type: TypeNode) {
2205+
console.log("createTypeSpread");
2206+
const node = <TypeSpreadTypeNode>createSynthesizedNode(SyntaxKind.TypeSpread);
2207+
node.type = type !== undefined ? parenthesizeElementTypeMember(type) : undefined;
2208+
return node;
2209+
}
2210+
2211+
export function updateTypeSpread(node: TypeSpreadTypeNode, type: TypeNode) {
2212+
return node.type !== type
2213+
? updateNode(createTypeSpread(type), node)
2214+
: node;
2215+
}
2216+
22042217
// Enum
22052218

22062219
export function createEnumMember(name: string | PropertyName, initializer?: Expression) {

src/compiler/parser.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ namespace ts {
8585
visitNode(cbNode, (<ShorthandPropertyAssignment>node).objectAssignmentInitializer);
8686
case SyntaxKind.SpreadAssignment:
8787
return visitNode(cbNode, (<SpreadAssignment>node).expression);
88+
case SyntaxKind.TypeSpread:
89+
return visitNode(cbNode, (<TypeSpreadTypeNode>node).type);
8890
case SyntaxKind.Parameter:
8991
case SyntaxKind.PropertyDeclaration:
9092
case SyntaxKind.PropertySignature:
@@ -1382,8 +1384,9 @@ namespace ts {
13821384
case ParsingContext.Parameters:
13831385
return isStartOfParameter();
13841386
case ParsingContext.TypeArguments:
1385-
case ParsingContext.TupleElementTypes:
13861387
return token() === SyntaxKind.CommaToken || isStartOfType();
1388+
case ParsingContext.TupleElementTypes:
1389+
return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isStartOfType();
13871390
case ParsingContext.HeritageClauses:
13881391
return isHeritageClause();
13891392
case ParsingContext.ImportOrExportSpecifiers:
@@ -2589,7 +2592,7 @@ namespace ts {
25892592

25902593
function parseTupleType(): TupleTypeNode {
25912594
const node = <TupleTypeNode>createNode(SyntaxKind.TupleType);
2592-
node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
2595+
node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseTupleElement, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
25932596
return finishNode(node);
25942597
}
25952598

@@ -5205,6 +5208,19 @@ namespace ts {
52055208
return finishNode(node);
52065209
}
52075210

5211+
function parseTupleElement(): TypeSpreadTypeNode | TypeNode {
5212+
return (token() === SyntaxKind.DotDotDotToken) ?
5213+
parseTypeSpread() :
5214+
parseType();
5215+
}
5216+
5217+
function parseTypeSpread(): TypeSpreadTypeNode {
5218+
const node = <TypeSpreadTypeNode>createNode(SyntaxKind.TypeSpread);
5219+
parseExpected(SyntaxKind.DotDotDotToken);
5220+
node.type = parseTypeOperatorOrHigher();
5221+
return finishNode(node);
5222+
}
5223+
52085224
function parseObjectBindingElement(): BindingElement {
52095225
const node = <BindingElement>createNode(SyntaxKind.BindingElement);
52105226
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);

src/compiler/types.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ namespace ts {
344344
PropertyAssignment,
345345
ShorthandPropertyAssignment,
346346
SpreadAssignment,
347+
TypeSpread,
347348

348349
// Enum
349350
EnumMember,
@@ -951,7 +952,12 @@ namespace ts {
951952

952953
export interface TupleTypeNode extends TypeNode {
953954
kind: SyntaxKind.TupleType;
954-
elementTypes: NodeArray<TypeNode>;
955+
elementTypes: NodeArray<TypeNode | TypeSpreadTypeNode>;
956+
}
957+
958+
export interface TypeSpreadTypeNode extends TypeNode {
959+
kind: SyntaxKind.TypeSpread;
960+
type: TypeNode;
955961
}
956962

957963
export type UnionOrIntersectionTypeNode = UnionTypeNode | IntersectionTypeNode;
@@ -3158,6 +3164,7 @@ namespace ts {
31583164
NonPrimitive = 1 << 24, // intrinsic object type
31593165
/* @internal */
31603166
JsxAttributes = 1 << 25, // Jsx attributes type
3167+
TypeSpread = 1 << 26, // spread in tuple types
31613168

31623169
/* @internal */
31633170
Nullable = Undefined | Null,
@@ -3406,6 +3413,11 @@ namespace ts {
34063413
constraint?: Type;
34073414
}
34083415

3416+
// type spread types (TypeFlags.TypeSpread)
3417+
export interface TypeSpreadType extends TypeVariable {
3418+
type: Type;
3419+
}
3420+
34093421
// keyof T types (TypeFlags.Index)
34103422
export interface IndexType extends Type {
34113423
type: TypeVariable | UnionOrIntersectionType;

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4506,6 +4506,10 @@ namespace ts {
45064506
return node.kind === SyntaxKind.SpreadAssignment;
45074507
}
45084508

4509+
export function isTypeSpread(node: Node): node is TypeSpreadTypeNode {
4510+
return node.kind === SyntaxKind.TypeSpread;
4511+
}
4512+
45094513
// Enum
45104514

45114515
export function isEnumMember(node: Node): node is EnumMember {

src/compiler/visitor.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,10 @@ namespace ts {
875875
return updateSpreadAssignment(<SpreadAssignment>node,
876876
visitNode((<SpreadAssignment>node).expression, visitor, isExpression));
877877

878+
case SyntaxKind.TypeSpread:
879+
return updateTypeSpread(<TypeSpreadTypeNode>node,
880+
visitNode((<TypeSpreadTypeNode>node).type, visitor, isTypeNode));
881+
878882
// Enum
879883
case SyntaxKind.EnumMember:
880884
return updateEnumMember(<EnumMember>node,
@@ -1401,6 +1405,10 @@ namespace ts {
14011405
result = reduceNode((<TypeCallTypeNode>node).type, cbNode, result);
14021406
break;
14031407

1408+
case SyntaxKind.TypeSpread:
1409+
result = reduceNode((<TypeSpreadTypeNode>node).type, cbNode, result);
1410+
break;
1411+
14041412
// Enum
14051413
case SyntaxKind.EnumMember:
14061414
result = reduceNode((<EnumMember>node).name, cbNode, result);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//// [tupleTypeSpread.ts]
2+
type a = [1, ...[2]];
3+
type Combine<Head, Tail extends any[]> = [Head, ...Tail];
4+
type b = Combine<1, [2, 3]>;
5+
6+
7+
//// [tupleTypeSpread.js]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
=== tests/cases/compiler/tupleTypeSpread.ts ===
2+
type a = [1, ...[2]];
3+
>a : Symbol(a, Decl(tupleTypeSpread.ts, 0, 0))
4+
5+
type Combine<Head, Tail extends any[]> = [Head, ...Tail];
6+
>Combine : Symbol(Combine, Decl(tupleTypeSpread.ts, 0, 21))
7+
>Head : Symbol(Head, Decl(tupleTypeSpread.ts, 1, 13))
8+
>Tail : Symbol(Tail, Decl(tupleTypeSpread.ts, 1, 18))
9+
>Head : Symbol(Head, Decl(tupleTypeSpread.ts, 1, 13))
10+
>Tail : Symbol(Tail, Decl(tupleTypeSpread.ts, 1, 18))
11+
12+
type b = Combine<1, [2, 3]>;
13+
>b : Symbol(b, Decl(tupleTypeSpread.ts, 1, 57))
14+
>Combine : Symbol(Combine, Decl(tupleTypeSpread.ts, 0, 21))
15+

0 commit comments

Comments
 (0)