diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c1011e57568..0a2df4f70d5 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3099,6 +3099,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; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf0c048ecbf..647bc7e3e5d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6002,6 +6002,8 @@ namespace ts { return getTypeFromTypeOperatorNode(node); case SyntaxKind.IndexedAccessType: return getTypeFromIndexedAccessTypeNode(node); + case SyntaxKind.MappedType: + return unknownType; // !!! // This function assumes that an identifier or qualified name is a type expression // Callers should first ensure this by calling isTypeNode case SyntaxKind.Identifier: @@ -15072,6 +15074,10 @@ namespace ts { getTypeFromIndexedAccessTypeNode(node); } + function checkMappedType(node: MappedTypeNode) { + node; // !!! + } + function isPrivateWithinAmbient(node: Node): boolean { return (getModifierFlags(node) & ModifierFlags.Private) && isInAmbientContext(node); } @@ -18312,6 +18318,8 @@ namespace ts { return checkSourceElement((node).type); case SyntaxKind.IndexedAccessType: return checkIndexedAccessType(node); + case SyntaxKind.IndexedAccessType: + return checkMappedType(node); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); case SyntaxKind.Block: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 92a3dcbef11..f4a41d8da12 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -139,6 +139,10 @@ namespace ts { case SyntaxKind.IndexedAccessType: return visitNode(cbNode, (node).objectType) || visitNode(cbNode, (node).indexType); + case SyntaxKind.MappedType: + return visitNode(cbNode, (node).iterationTypeName) || + visitNode(cbNode, (node).indexType) || + visitNode(cbNode, (node).type); case SyntaxKind.LiteralType: return visitNode(cbNode, (node).literal); case SyntaxKind.ObjectBindingPattern: @@ -2399,6 +2403,30 @@ namespace ts { return members; } + function isStartOfMappedType() { + nextToken(); + if (token() === SyntaxKind.ReadonlyKeyword) { + nextToken(); + } + return token() === SyntaxKind.OpenBracketToken && nextTokenIsIdentifier() && nextToken() === SyntaxKind.InKeyword; + } + + function parseMappedType() { + const node = createNode(SyntaxKind.MappedType); + parseExpected(SyntaxKind.OpenBraceToken); + node.readonlyToken = parseOptionalToken(SyntaxKind.ReadonlyKeyword); + parseExpected(SyntaxKind.OpenBracketToken); + node.iterationTypeName = parseIdentifier(); + parseExpected(SyntaxKind.InKeyword); + node.indexType = parseType(); + parseExpected(SyntaxKind.CloseBracketToken); + node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken); + node.type = parseTypeAnnotation(); + parseSemicolon(); + parseExpected(SyntaxKind.CloseBraceToken); + return finishNode(node); + } + function parseTupleType(): TupleTypeNode { const node = createNode(SyntaxKind.TupleType); node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); @@ -2472,7 +2500,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: diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 1362746ba57..65e31ab1ee6 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -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. @@ -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: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 381289b133b..22fbe29c76b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -219,6 +219,7 @@ namespace ts { ThisType, TypeOperator, IndexedAccessType, + MappedType, LiteralType, // Binding patterns ObjectBindingPattern, @@ -519,6 +520,7 @@ namespace ts { export type EqualsGreaterThanToken = Token; export type EndOfFileToken = Token; export type AtToken = Token; + export type ReadonlyToken = Token; export type Modifier = Token @@ -897,6 +899,15 @@ namespace ts { indexType: TypeNode; } + export interface MappedTypeNode extends TypeNode { + kind: SyntaxKind.MappedType; + readonlyToken?: ReadonlyToken; + iterationTypeName: Identifier; + indexType: TypeNode; + questionToken?: QuestionToken; + type?: TypeNode; + } + export interface LiteralTypeNode extends TypeNode { kind: SyntaxKind.LiteralType; literal: Expression;