Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapped types #12114

Merged
merged 31 commits into from
Nov 13, 2016
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7cd39e3
Parsing of mapped types
ahejlsberg Nov 3, 2016
d1a8af5
Parse '[P in K]' part of mapped type as a type parameter declaration
ahejlsberg Nov 3, 2016
fc450a2
Introduce MappedType in type checker
ahejlsberg Nov 4, 2016
ecdb74c
Merge branch 'master' into mappedTypes
ahejlsberg Nov 4, 2016
7807ac9
Attach symbols to mapped types
ahejlsberg Nov 4, 2016
1c7b397
Introduce instantiateCached function
ahejlsberg Nov 5, 2016
507ab30
Handle readonly and optional properties + index signatures
ahejlsberg Nov 6, 2016
2564e1c
Handle recursion in mapped type display
ahejlsberg Nov 6, 2016
5de63a7
Validate constraint type in mapped type
ahejlsberg Nov 7, 2016
de93876
Correct symbol display for type parameter of mapped type
ahejlsberg Nov 7, 2016
9f3aa38
Improve sharing by re-instantiating top level type aliases
ahejlsberg Nov 7, 2016
8aef1e6
Type inference for mapped types
ahejlsberg Nov 8, 2016
7ca5923
Merge branch 'master' into mappedTypes
ahejlsberg Nov 8, 2016
a562d6e
Make keyof T assignable to and subtype of string | number
ahejlsberg Nov 8, 2016
cf2953b
Add relations for keyof S / keyof T and [P in S]: X / [P in T]: X
ahejlsberg Nov 8, 2016
aca7e2f
Don't include private/protected properties in keyof T
ahejlsberg Nov 8, 2016
3dd11e4
Properly implement type relationship for '[P in S]: X' and '[P in T]: Y'
ahejlsberg Nov 9, 2016
2170ff6
Defer resolution of mapped types to enable recursive definitions
ahejlsberg Nov 10, 2016
b81c226
Use pull model to obtain type alias information for type nodes
ahejlsberg Nov 10, 2016
de2da2c
Accept new baselines
ahejlsberg Nov 10, 2016
aca1ab3
Check mapped type constraint is assignable to string | number
ahejlsberg Nov 10, 2016
64d2698
Merge branch 'master' into mappedTypes
ahejlsberg Nov 10, 2016
cd185f2
Add declaration emit support
ahejlsberg Nov 10, 2016
1c7ec6b
Add missing node visits in forEachChild
ahejlsberg Nov 11, 2016
364142c
Improve type inference for types with same generic type alias
ahejlsberg Nov 11, 2016
e9b6ddc
Add tests
ahejlsberg Nov 11, 2016
ca3f797
More tests
ahejlsberg Nov 11, 2016
5028a44
Accept new baselines
ahejlsberg Nov 11, 2016
9ac7667
Address CR feedback
ahejlsberg Nov 12, 2016
6ceab7b
Accept new baselines
ahejlsberg Nov 12, 2016
cd05c07
Add comment explaining type alias instantiation strategy
ahejlsberg Nov 13, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,7 @@ namespace ts {
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.MappedType:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MappedType seems to behave like a TypeLiteral elsewhere, but I'm not why it has HasLocals here like TypeAliasDeclaration does. Is it that MappedType introduces a new name for the parameter type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because of the type parameter introduced by [P in ...]. We want that symbol entered into the locals symbol table such that it is accessible within the mapped type, but not from the outside (unlike members).

return ContainerFlags.IsContainer | ContainerFlags.HasLocals;

case SyntaxKind.SourceFile:
Expand Down Expand Up @@ -1427,6 +1428,7 @@ namespace ts {
case SyntaxKind.ArrowFunction:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.MappedType:
// All the children of these container types are never visible through another
// symbol (i.e. through another symbol's 'exports' or 'members'). Instead,
// they're only accessed 'lexically' (i.e. from code that exists underneath
Expand Down Expand Up @@ -1977,9 +1979,10 @@ namespace ts {
case SyntaxKind.JSDocFunctionType:
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
case SyntaxKind.TypeLiteral:
case SyntaxKind.MappedType:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocRecordType:
return bindAnonymousDeclaration(<TypeLiteralNode>node, SymbolFlags.TypeLiteral, "__type");
return bindAnonymousDeclaration(<Declaration>node, SymbolFlags.TypeLiteral, "__type");
case SyntaxKind.ObjectLiteralExpression:
return bindObjectLiteralExpression(<ObjectLiteralExpression>node);
case SyntaxKind.FunctionExpression:
Expand Down Expand Up @@ -3153,6 +3156,7 @@ namespace ts {
case SyntaxKind.ThisType:
case SyntaxKind.TypeOperator:
case SyntaxKind.IndexedAccessType:
case SyntaxKind.MappedType:
case SyntaxKind.LiteralType:
// Types and signatures are TypeScript syntax, and exclude all other facts.
transformFlags = TransformFlags.AssertTypeScript;
Expand Down
418 changes: 320 additions & 98 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

32 changes: 30 additions & 2 deletions src/compiler/declarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,9 @@ namespace ts {
case SyntaxKind.TypeOperator:
return emitTypeOperator(<TypeOperatorNode>type);
case SyntaxKind.IndexedAccessType:
return emitPropertyAccessType(<IndexedAccessTypeNode>type);
return emitIndexedAccessType(<IndexedAccessTypeNode>type);
case SyntaxKind.MappedType:
return emitMappedType(<MappedTypeNode>type);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
return emitSignatureDeclarationWithJsDocComments(<FunctionOrConstructorTypeNode>type);
Expand Down Expand Up @@ -516,13 +518,39 @@ namespace ts {
emitType(type.type);
}

function emitPropertyAccessType(node: IndexedAccessTypeNode) {
function emitIndexedAccessType(node: IndexedAccessTypeNode) {
emitType(node.objectType);
write("[");
emitType(node.indexType);
write("]");
}

function emitMappedType(node: MappedTypeNode) {
const prevEnclosingDeclaration = enclosingDeclaration;
enclosingDeclaration = node;
write("{");
writeLine();
increaseIndent();
if (node.readonlyToken) {
write("readonly ");
}
write("[");
writeEntityName(node.typeParameter.name);
write(" in ");
emitType(node.typeParameter.constraint);
write("]");
if (node.questionToken) {
write("?");
}
write(": ");
emitType(node.type);
write(";");
writeLine();
decreaseIndent();
write("}");
enclosingDeclaration = prevEnclosingDeclaration;
}

function emitTypeLiteral(type: TypeLiteralNode) {
write("{");
if (type.members.length) {
Expand Down
29 changes: 27 additions & 2 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,9 @@ const _super = (function (geti, seti) {
case SyntaxKind.TypeOperator:
return emitTypeOperator(<TypeOperatorNode>node);
case SyntaxKind.IndexedAccessType:
return emitPropertyAccessType(<IndexedAccessTypeNode>node);
return emitIndexedAccessType(<IndexedAccessTypeNode>node);
case SyntaxKind.MappedType:
return emitMappedType(<MappedTypeNode>node);
case SyntaxKind.LiteralType:
return emitLiteralType(<LiteralTypeNode>node);

Expand Down Expand Up @@ -1109,13 +1111,36 @@ const _super = (function (geti, seti) {
emit(node.type);
}

function emitPropertyAccessType(node: IndexedAccessTypeNode) {
function emitIndexedAccessType(node: IndexedAccessTypeNode) {
emit(node.objectType);
write("[");
emit(node.indexType);
write("]");
}

function emitMappedType(node: MappedTypeNode) {
write("{");
writeLine();
increaseIndent();
if (node.readonlyToken) {
write("readonly ");
}
write("[");
emit(node.typeParameter.name);
write(" in ");
emit(node.typeParameter.constraint);
write("]");
if (node.questionToken) {
write("?");
}
write(": ");
emit(node.type);
write(";");
writeLine();
decreaseIndent();
write("}");
}

function emitLiteralType(node: LiteralTypeNode) {
emitExpression(node.literal);
}
Expand Down
37 changes: 36 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ namespace ts {
case SyntaxKind.IndexedAccessType:
return visitNode(cbNode, (<IndexedAccessTypeNode>node).objectType) ||
visitNode(cbNode, (<IndexedAccessTypeNode>node).indexType);
case SyntaxKind.MappedType:
return visitNode(cbNode, (<MappedTypeNode>node).readonlyToken) ||
visitNode(cbNode, (<MappedTypeNode>node).typeParameter) ||
visitNode(cbNode, (<MappedTypeNode>node).questionToken) ||
visitNode(cbNode, (<MappedTypeNode>node).type);
case SyntaxKind.LiteralType:
return visitNode(cbNode, (<LiteralTypeNode>node).literal);
case SyntaxKind.ObjectBindingPattern:
Expand Down Expand Up @@ -2422,6 +2427,36 @@ namespace ts {
return members;
}

function isStartOfMappedType() {
nextToken();
if (token() === SyntaxKind.ReadonlyKeyword) {
nextToken();
}
return token() === SyntaxKind.OpenBracketToken && nextTokenIsIdentifier() && nextToken() === SyntaxKind.InKeyword;
}

function parseMappedTypeParameter() {
const node = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
node.name = parseIdentifier();
parseExpected(SyntaxKind.InKeyword);
node.constraint = parseType();
return finishNode(node);
}

function parseMappedType() {
const node = <MappedTypeNode>createNode(SyntaxKind.MappedType);
parseExpected(SyntaxKind.OpenBraceToken);
node.readonlyToken = parseOptionalToken(SyntaxKind.ReadonlyKeyword);
parseExpected(SyntaxKind.OpenBracketToken);
node.typeParameter = parseMappedTypeParameter();
parseExpected(SyntaxKind.CloseBracketToken);
node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
node.type = parseTypeAnnotation();
parseSemicolon();
parseExpected(SyntaxKind.CloseBraceToken);
return finishNode(node);
}

function parseTupleType(): TupleTypeNode {
const node = <TupleTypeNode>createNode(SyntaxKind.TupleType);
node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
Expand Down Expand Up @@ -2495,7 +2530,7 @@ namespace ts {
case SyntaxKind.TypeOfKeyword:
return parseTypeQuery();
case SyntaxKind.OpenBraceToken:
return parseTypeLiteral();
return lookAhead(isStartOfMappedType) ? parseMappedType() : parseTypeLiteral();
case SyntaxKind.OpenBracketToken:
return parseTupleType();
case SyntaxKind.OpenParenToken:
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ namespace ts {
case SyntaxKind.ThisType:
case SyntaxKind.TypeOperator:
case SyntaxKind.IndexedAccessType:
case SyntaxKind.MappedType:
case SyntaxKind.LiteralType:
// TypeScript type nodes are elided.

Expand Down Expand Up @@ -1787,6 +1788,7 @@ namespace ts {
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeOperator:
case SyntaxKind.IndexedAccessType:
case SyntaxKind.MappedType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.AnyKeyword:
case SyntaxKind.ThisType:
Expand Down
30 changes: 25 additions & 5 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ namespace ts {
ThisType,
TypeOperator,
IndexedAccessType,
MappedType,
LiteralType,
// Binding patterns
ObjectBindingPattern,
Expand Down Expand Up @@ -520,6 +521,7 @@ namespace ts {
export type EqualsGreaterThanToken = Token<SyntaxKind.EqualsGreaterThanToken>;
export type EndOfFileToken = Token<SyntaxKind.EndOfFileToken>;
export type AtToken = Token<SyntaxKind.AtToken>;
export type ReadonlyToken = Token<SyntaxKind.ReadonlyKeyword>;

export type Modifier
= Token<SyntaxKind.AbstractKeyword>
Expand Down Expand Up @@ -903,6 +905,14 @@ namespace ts {
indexType: TypeNode;
}

export interface MappedTypeNode extends TypeNode, Declaration {
kind: SyntaxKind.MappedType;
readonlyToken?: ReadonlyToken;
typeParameter: TypeParameterDeclaration;
questionToken?: QuestionToken;
type?: TypeNode;
}

export interface LiteralTypeNode extends TypeNode {
kind: SyntaxKind.LiteralType;
literal: Expression;
Expand Down Expand Up @@ -2501,7 +2511,7 @@ namespace ts {
RegularEnum = 0x00000100, // Enum
ValueModule = 0x00000200, // Instantiated module
NamespaceModule = 0x00000400, // Uninstantiated module
TypeLiteral = 0x00000800, // Type Literal
TypeLiteral = 0x00000800, // Type Literal or mapped type
ObjectLiteral = 0x00001000, // Object Literal
Method = 0x00002000, // Method
Constructor = 0x00004000, // Constructor
Expand Down Expand Up @@ -2779,10 +2789,11 @@ namespace ts {
Reference = 1 << 2, // Generic type reference
Tuple = 1 << 3, // Synthesized generic tuple type
Anonymous = 1 << 4, // Anonymous
Instantiated = 1 << 5, // Instantiated anonymous type
ObjectLiteral = 1 << 6, // Originates in an object literal
EvolvingArray = 1 << 7, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 8, // Object literal pattern with computed properties
Mapped = 1 << 5, // Mapped
Instantiated = 1 << 6, // Instantiated anonymous or mapped type
ObjectLiteral = 1 << 7, // Originates in an object literal
EvolvingArray = 1 << 8, // Evolving array type
ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties
ClassOrInterface = Class | Interface
}

Expand Down Expand Up @@ -2851,6 +2862,15 @@ namespace ts {
mapper?: TypeMapper; // Instantiation mapper
}

/* @internal */
export interface MappedType extends ObjectType {
declaration: MappedTypeNode;
typeParameter?: TypeParameter;
constraintType?: Type;
templateType?: Type;
mapper?: TypeMapper; // Instantiation mapper
}

export interface EvolvingArrayType extends ObjectType {
elementType: Type; // Element expressions of evolving array type
finalArrayType?: Type; // Final array type of evolving array type
Expand Down
14 changes: 10 additions & 4 deletions src/services/symbolDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,9 @@ namespace ts.SymbolDisplay {
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
displayParts.push(spacePart());
addFullSymbolName(symbol);
displayParts.push(spacePart());
displayParts.push(keywordPart(SyntaxKind.InKeyword));
displayParts.push(spacePart());
if (symbol.parent) {
// Class/Interface type parameter
addInPrefix();
addFullSymbolName(symbol.parent, enclosingDeclaration);
writeTypeParametersOfSymbol(symbol.parent, enclosingDeclaration);
}
Expand All @@ -288,6 +286,7 @@ namespace ts.SymbolDisplay {

if (declaration) {
if (isFunctionLikeKind(declaration.kind)) {
addInPrefix();
const signature = typeChecker.getSignatureFromDeclaration(<SignatureDeclaration>declaration);
if (declaration.kind === SyntaxKind.ConstructSignature) {
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
Expand All @@ -298,10 +297,11 @@ namespace ts.SymbolDisplay {
}
addRange(displayParts, signatureToDisplayParts(typeChecker, signature, sourceFile, TypeFormatFlags.WriteTypeArgumentsOfSignature));
}
else {
else if (declaration.kind === SyntaxKind.TypeAliasDeclaration) {
// Type alias type parameter
// For example
// type list<T> = T[]; // Both T will go through same code path
addInPrefix();
displayParts.push(keywordPart(SyntaxKind.TypeKeyword));
displayParts.push(spacePart());
addFullSymbolName(declaration.symbol);
Expand Down Expand Up @@ -439,6 +439,12 @@ namespace ts.SymbolDisplay {
}
}

function addInPrefix() {
displayParts.push(spacePart());
displayParts.push(keywordPart(SyntaxKind.InKeyword));
displayParts.push(spacePart());
}

function addFullSymbolName(symbol: Symbol, enclosingDeclaration?: Node) {
const fullSymbolDisplayParts = symbolToDisplayParts(typeChecker, symbol, enclosingDeclaration || sourceFile, /*meaning*/ undefined,
SymbolFormatFlags.WriteTypeParametersOrArguments | SymbolFormatFlags.UseOnlyExternalAliasing);
Expand Down
Loading