Skip to content

Commit f437c8f

Browse files
authored
Merge pull request #12114 from Microsoft/mappedTypes
Mapped types
2 parents 4166eeb + cd05c07 commit f437c8f

33 files changed

+2757
-171
lines changed

src/compiler/binder.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ namespace ts {
13081308
case SyntaxKind.JSDocFunctionType:
13091309
case SyntaxKind.ModuleDeclaration:
13101310
case SyntaxKind.TypeAliasDeclaration:
1311+
case SyntaxKind.MappedType:
13111312
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
13121313

13131314
case SyntaxKind.SourceFile:
@@ -1427,6 +1428,7 @@ namespace ts {
14271428
case SyntaxKind.ArrowFunction:
14281429
case SyntaxKind.JSDocFunctionType:
14291430
case SyntaxKind.TypeAliasDeclaration:
1431+
case SyntaxKind.MappedType:
14301432
// All the children of these container types are never visible through another
14311433
// symbol (i.e. through another symbol's 'exports' or 'members'). Instead,
14321434
// they're only accessed 'lexically' (i.e. from code that exists underneath
@@ -1977,9 +1979,10 @@ namespace ts {
19771979
case SyntaxKind.JSDocFunctionType:
19781980
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
19791981
case SyntaxKind.TypeLiteral:
1982+
case SyntaxKind.MappedType:
19801983
case SyntaxKind.JSDocTypeLiteral:
19811984
case SyntaxKind.JSDocRecordType:
1982-
return bindAnonymousDeclaration(<TypeLiteralNode>node, SymbolFlags.TypeLiteral, "__type");
1985+
return bindAnonymousDeclaration(<Declaration>node, SymbolFlags.TypeLiteral, "__type");
19831986
case SyntaxKind.ObjectLiteralExpression:
19841987
return bindObjectLiteralExpression(<ObjectLiteralExpression>node);
19851988
case SyntaxKind.FunctionExpression:
@@ -3153,6 +3156,7 @@ namespace ts {
31533156
case SyntaxKind.ThisType:
31543157
case SyntaxKind.TypeOperator:
31553158
case SyntaxKind.IndexedAccessType:
3159+
case SyntaxKind.MappedType:
31563160
case SyntaxKind.LiteralType:
31573161
// Types and signatures are TypeScript syntax, and exclude all other facts.
31583162
transformFlags = TransformFlags.AssertTypeScript;

src/compiler/checker.ts

Lines changed: 343 additions & 98 deletions
Large diffs are not rendered by default.

src/compiler/declarationEmitter.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,9 @@ namespace ts {
416416
case SyntaxKind.TypeOperator:
417417
return emitTypeOperator(<TypeOperatorNode>type);
418418
case SyntaxKind.IndexedAccessType:
419-
return emitPropertyAccessType(<IndexedAccessTypeNode>type);
419+
return emitIndexedAccessType(<IndexedAccessTypeNode>type);
420+
case SyntaxKind.MappedType:
421+
return emitMappedType(<MappedTypeNode>type);
420422
case SyntaxKind.FunctionType:
421423
case SyntaxKind.ConstructorType:
422424
return emitSignatureDeclarationWithJsDocComments(<FunctionOrConstructorTypeNode>type);
@@ -516,13 +518,39 @@ namespace ts {
516518
emitType(type.type);
517519
}
518520

519-
function emitPropertyAccessType(node: IndexedAccessTypeNode) {
521+
function emitIndexedAccessType(node: IndexedAccessTypeNode) {
520522
emitType(node.objectType);
521523
write("[");
522524
emitType(node.indexType);
523525
write("]");
524526
}
525527

528+
function emitMappedType(node: MappedTypeNode) {
529+
const prevEnclosingDeclaration = enclosingDeclaration;
530+
enclosingDeclaration = node;
531+
write("{");
532+
writeLine();
533+
increaseIndent();
534+
if (node.readonlyToken) {
535+
write("readonly ");
536+
}
537+
write("[");
538+
writeEntityName(node.typeParameter.name);
539+
write(" in ");
540+
emitType(node.typeParameter.constraint);
541+
write("]");
542+
if (node.questionToken) {
543+
write("?");
544+
}
545+
write(": ");
546+
emitType(node.type);
547+
write(";");
548+
writeLine();
549+
decreaseIndent();
550+
write("}");
551+
enclosingDeclaration = prevEnclosingDeclaration;
552+
}
553+
526554
function emitTypeLiteral(type: TypeLiteralNode) {
527555
write("{");
528556
if (type.members.length) {

src/compiler/emitter.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,9 @@ const _super = (function (geti, seti) {
606606
case SyntaxKind.TypeOperator:
607607
return emitTypeOperator(<TypeOperatorNode>node);
608608
case SyntaxKind.IndexedAccessType:
609-
return emitPropertyAccessType(<IndexedAccessTypeNode>node);
609+
return emitIndexedAccessType(<IndexedAccessTypeNode>node);
610+
case SyntaxKind.MappedType:
611+
return emitMappedType(<MappedTypeNode>node);
610612
case SyntaxKind.LiteralType:
611613
return emitLiteralType(<LiteralTypeNode>node);
612614

@@ -1109,13 +1111,36 @@ const _super = (function (geti, seti) {
11091111
emit(node.type);
11101112
}
11111113

1112-
function emitPropertyAccessType(node: IndexedAccessTypeNode) {
1114+
function emitIndexedAccessType(node: IndexedAccessTypeNode) {
11131115
emit(node.objectType);
11141116
write("[");
11151117
emit(node.indexType);
11161118
write("]");
11171119
}
11181120

1121+
function emitMappedType(node: MappedTypeNode) {
1122+
write("{");
1123+
writeLine();
1124+
increaseIndent();
1125+
if (node.readonlyToken) {
1126+
write("readonly ");
1127+
}
1128+
write("[");
1129+
emit(node.typeParameter.name);
1130+
write(" in ");
1131+
emit(node.typeParameter.constraint);
1132+
write("]");
1133+
if (node.questionToken) {
1134+
write("?");
1135+
}
1136+
write(": ");
1137+
emit(node.type);
1138+
write(";");
1139+
writeLine();
1140+
decreaseIndent();
1141+
write("}");
1142+
}
1143+
11191144
function emitLiteralType(node: LiteralTypeNode) {
11201145
emitExpression(node.literal);
11211146
}

src/compiler/parser.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ namespace ts {
141141
case SyntaxKind.IndexedAccessType:
142142
return visitNode(cbNode, (<IndexedAccessTypeNode>node).objectType) ||
143143
visitNode(cbNode, (<IndexedAccessTypeNode>node).indexType);
144+
case SyntaxKind.MappedType:
145+
return visitNode(cbNode, (<MappedTypeNode>node).readonlyToken) ||
146+
visitNode(cbNode, (<MappedTypeNode>node).typeParameter) ||
147+
visitNode(cbNode, (<MappedTypeNode>node).questionToken) ||
148+
visitNode(cbNode, (<MappedTypeNode>node).type);
144149
case SyntaxKind.LiteralType:
145150
return visitNode(cbNode, (<LiteralTypeNode>node).literal);
146151
case SyntaxKind.ObjectBindingPattern:
@@ -2422,6 +2427,36 @@ namespace ts {
24222427
return members;
24232428
}
24242429

2430+
function isStartOfMappedType() {
2431+
nextToken();
2432+
if (token() === SyntaxKind.ReadonlyKeyword) {
2433+
nextToken();
2434+
}
2435+
return token() === SyntaxKind.OpenBracketToken && nextTokenIsIdentifier() && nextToken() === SyntaxKind.InKeyword;
2436+
}
2437+
2438+
function parseMappedTypeParameter() {
2439+
const node = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
2440+
node.name = parseIdentifier();
2441+
parseExpected(SyntaxKind.InKeyword);
2442+
node.constraint = parseType();
2443+
return finishNode(node);
2444+
}
2445+
2446+
function parseMappedType() {
2447+
const node = <MappedTypeNode>createNode(SyntaxKind.MappedType);
2448+
parseExpected(SyntaxKind.OpenBraceToken);
2449+
node.readonlyToken = parseOptionalToken(SyntaxKind.ReadonlyKeyword);
2450+
parseExpected(SyntaxKind.OpenBracketToken);
2451+
node.typeParameter = parseMappedTypeParameter();
2452+
parseExpected(SyntaxKind.CloseBracketToken);
2453+
node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
2454+
node.type = parseTypeAnnotation();
2455+
parseSemicolon();
2456+
parseExpected(SyntaxKind.CloseBraceToken);
2457+
return finishNode(node);
2458+
}
2459+
24252460
function parseTupleType(): TupleTypeNode {
24262461
const node = <TupleTypeNode>createNode(SyntaxKind.TupleType);
24272462
node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
@@ -2495,7 +2530,7 @@ namespace ts {
24952530
case SyntaxKind.TypeOfKeyword:
24962531
return parseTypeQuery();
24972532
case SyntaxKind.OpenBraceToken:
2498-
return parseTypeLiteral();
2533+
return lookAhead(isStartOfMappedType) ? parseMappedType() : parseTypeLiteral();
24992534
case SyntaxKind.OpenBracketToken:
25002535
return parseTupleType();
25012536
case SyntaxKind.OpenParenToken:

src/compiler/transformers/ts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ namespace ts {
302302
case SyntaxKind.ThisType:
303303
case SyntaxKind.TypeOperator:
304304
case SyntaxKind.IndexedAccessType:
305+
case SyntaxKind.MappedType:
305306
case SyntaxKind.LiteralType:
306307
// TypeScript type nodes are elided.
307308

@@ -1791,6 +1792,7 @@ namespace ts {
17911792
case SyntaxKind.TypeQuery:
17921793
case SyntaxKind.TypeOperator:
17931794
case SyntaxKind.IndexedAccessType:
1795+
case SyntaxKind.MappedType:
17941796
case SyntaxKind.TypeLiteral:
17951797
case SyntaxKind.AnyKeyword:
17961798
case SyntaxKind.ThisType:

src/compiler/types.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ namespace ts {
219219
ThisType,
220220
TypeOperator,
221221
IndexedAccessType,
222+
MappedType,
222223
LiteralType,
223224
// Binding patterns
224225
ObjectBindingPattern,
@@ -520,6 +521,7 @@ namespace ts {
520521
export type EqualsGreaterThanToken = Token<SyntaxKind.EqualsGreaterThanToken>;
521522
export type EndOfFileToken = Token<SyntaxKind.EndOfFileToken>;
522523
export type AtToken = Token<SyntaxKind.AtToken>;
524+
export type ReadonlyToken = Token<SyntaxKind.ReadonlyKeyword>;
523525

524526
export type Modifier
525527
= Token<SyntaxKind.AbstractKeyword>
@@ -903,6 +905,14 @@ namespace ts {
903905
indexType: TypeNode;
904906
}
905907

908+
export interface MappedTypeNode extends TypeNode, Declaration {
909+
kind: SyntaxKind.MappedType;
910+
readonlyToken?: ReadonlyToken;
911+
typeParameter: TypeParameterDeclaration;
912+
questionToken?: QuestionToken;
913+
type?: TypeNode;
914+
}
915+
906916
export interface LiteralTypeNode extends TypeNode {
907917
kind: SyntaxKind.LiteralType;
908918
literal: Expression;
@@ -2501,7 +2511,7 @@ namespace ts {
25012511
RegularEnum = 0x00000100, // Enum
25022512
ValueModule = 0x00000200, // Instantiated module
25032513
NamespaceModule = 0x00000400, // Uninstantiated module
2504-
TypeLiteral = 0x00000800, // Type Literal
2514+
TypeLiteral = 0x00000800, // Type Literal or mapped type
25052515
ObjectLiteral = 0x00001000, // Object Literal
25062516
Method = 0x00002000, // Method
25072517
Constructor = 0x00004000, // Constructor
@@ -2780,10 +2790,11 @@ namespace ts {
27802790
Reference = 1 << 2, // Generic type reference
27812791
Tuple = 1 << 3, // Synthesized generic tuple type
27822792
Anonymous = 1 << 4, // Anonymous
2783-
Instantiated = 1 << 5, // Instantiated anonymous type
2784-
ObjectLiteral = 1 << 6, // Originates in an object literal
2785-
EvolvingArray = 1 << 7, // Evolving array type
2786-
ObjectLiteralPatternWithComputedProperties = 1 << 8, // Object literal pattern with computed properties
2793+
Mapped = 1 << 5, // Mapped
2794+
Instantiated = 1 << 6, // Instantiated anonymous or mapped type
2795+
ObjectLiteral = 1 << 7, // Originates in an object literal
2796+
EvolvingArray = 1 << 8, // Evolving array type
2797+
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
27872798
ClassOrInterface = Class | Interface
27882799
}
27892800

@@ -2852,6 +2863,15 @@ namespace ts {
28522863
mapper?: TypeMapper; // Instantiation mapper
28532864
}
28542865

2866+
/* @internal */
2867+
export interface MappedType extends ObjectType {
2868+
declaration: MappedTypeNode;
2869+
typeParameter?: TypeParameter;
2870+
constraintType?: Type;
2871+
templateType?: Type;
2872+
mapper?: TypeMapper; // Instantiation mapper
2873+
}
2874+
28552875
export interface EvolvingArrayType extends ObjectType {
28562876
elementType: Type; // Element expressions of evolving array type
28572877
finalArrayType?: Type; // Final array type of evolving array type

src/services/symbolDisplay.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,11 +272,9 @@ namespace ts.SymbolDisplay {
272272
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
273273
displayParts.push(spacePart());
274274
addFullSymbolName(symbol);
275-
displayParts.push(spacePart());
276-
displayParts.push(keywordPart(SyntaxKind.InKeyword));
277-
displayParts.push(spacePart());
278275
if (symbol.parent) {
279276
// Class/Interface type parameter
277+
addInPrefix();
280278
addFullSymbolName(symbol.parent, enclosingDeclaration);
281279
writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration);
282280
}
@@ -288,6 +286,7 @@ namespace ts.SymbolDisplay {
288286

289287
if (declaration) {
290288
if (isFunctionLikeKind(declaration.kind)) {
289+
addInPrefix();
291290
const signature = typeChecker.getSignatureFromDeclaration(<SignatureDeclaration>declaration);
292291
if (declaration.kind === SyntaxKind.ConstructSignature) {
293292
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
@@ -298,10 +297,11 @@ namespace ts.SymbolDisplay {
298297
}
299298
addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature));
300299
}
301-
else {
300+
else if (declaration.kind === SyntaxKind.TypeAliasDeclaration) {
302301
// Type alias type parameter
303302
// For example
304303
// type list<T> = T[]; // Both T will go through same code path
304+
addInPrefix();
305305
displayParts.push(keywordPart(SyntaxKind.TypeKeyword));
306306
displayParts.push(spacePart());
307307
addFullSymbolName(declaration.symbol);
@@ -439,6 +439,12 @@ namespace ts.SymbolDisplay {
439439
}
440440
}
441441

442+
function addInPrefix() {
443+
displayParts.push(spacePart());
444+
displayParts.push(keywordPart(SyntaxKind.InKeyword));
445+
displayParts.push(spacePart());
446+
}
447+
442448
function addFullSymbolName(symbol: Symbol, enclosingDeclaration?: Node) {
443449
const fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbol, enclosingDeclaration || sourceFile, /*meaning*/ undefined,
444450
SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing);

0 commit comments

Comments
 (0)