From 7cd39e381837368058cdad30edd7edf45fe94de2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Nov 2016 06:09:44 -0700 Subject: [PATCH 01/28] Parsing of mapped types --- src/compiler/binder.ts | 1 + src/compiler/checker.ts | 8 ++++++++ src/compiler/parser.ts | 30 +++++++++++++++++++++++++++++- src/compiler/transformers/ts.ts | 2 ++ src/compiler/types.ts | 11 +++++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index c1011e575683b..0a2df4f70d5e0 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 cf0c048ecbfb8..647bc7e3e5d12 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 92a3dcbef11c7..f4a41d8da12be 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 1362746ba57a8..65e31ab1ee619 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 381289b133b49..22fbe29c76b3d 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; From d1a8af532092378b07e3d441838070e5b5360466 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Nov 2016 14:35:27 -0700 Subject: [PATCH 02/28] Parse '[P in K]' part of mapped type as a type parameter declaration --- src/compiler/binder.ts | 2 ++ src/compiler/checker.ts | 18 +++++++++++++++--- src/compiler/parser.ts | 15 ++++++++++----- src/compiler/types.ts | 3 +-- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 0a2df4f70d5e0..e9419ab1cc64c 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1302,6 +1302,7 @@ namespace ts { case SyntaxKind.JSDocFunctionType: case SyntaxKind.ModuleDeclaration: case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.MappedType: return ContainerFlags.IsContainer | ContainerFlags.HasLocals; case SyntaxKind.SourceFile: @@ -1421,6 +1422,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 diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 647bc7e3e5d12..9e1a0b6ec7bdb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5834,6 +5834,18 @@ namespace ts { return links.resolvedType; } + function getTypeFromMappedTypeNode(node: MappedTypeNode) { + const links = getNodeLinks(node); + if (!links.resolvedType) { + getTypeFromTypeNode(node.typeParameter.constraint); + if (node.type) { + getTypeFromTypeNode(node.type); + } + links.resolvedType = unknownType; + } + return links.resolvedType; + } + function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { const links = getNodeLinks(node); if (!links.resolvedType) { @@ -6003,7 +6015,7 @@ namespace ts { case SyntaxKind.IndexedAccessType: return getTypeFromIndexedAccessTypeNode(node); case SyntaxKind.MappedType: - return unknownType; // !!! + return getTypeFromMappedTypeNode(node); // This function assumes that an identifier or qualified name is a type expression // Callers should first ensure this by calling isTypeNode case SyntaxKind.Identifier: @@ -15075,7 +15087,7 @@ namespace ts { } function checkMappedType(node: MappedTypeNode) { - node; // !!! + getTypeFromMappedTypeNode(node); } function isPrivateWithinAmbient(node: Node): boolean { @@ -18318,7 +18330,7 @@ namespace ts { return checkSourceElement((node).type); case SyntaxKind.IndexedAccessType: return checkIndexedAccessType(node); - case SyntaxKind.IndexedAccessType: + case SyntaxKind.MappedType: return checkMappedType(node); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f4a41d8da12be..a685baea3e946 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -140,8 +140,7 @@ namespace ts { return visitNode(cbNode, (node).objectType) || visitNode(cbNode, (node).indexType); case SyntaxKind.MappedType: - return visitNode(cbNode, (node).iterationTypeName) || - visitNode(cbNode, (node).indexType) || + return visitNode(cbNode, (node).typeParameter) || visitNode(cbNode, (node).type); case SyntaxKind.LiteralType: return visitNode(cbNode, (node).literal); @@ -2411,14 +2410,20 @@ namespace ts { return token() === SyntaxKind.OpenBracketToken && nextTokenIsIdentifier() && nextToken() === SyntaxKind.InKeyword; } + function parseMappedTypeParameter() { + const node = createNode(SyntaxKind.TypeParameter); + node.name = parseIdentifier(); + parseExpected(SyntaxKind.InKeyword); + node.constraint = parseType(); + return finishNode(node); + } + 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(); + node.typeParameter = parseMappedTypeParameter(); parseExpected(SyntaxKind.CloseBracketToken); node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken); node.type = parseTypeAnnotation(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 22fbe29c76b3d..6858d028377ba 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -902,8 +902,7 @@ namespace ts { export interface MappedTypeNode extends TypeNode { kind: SyntaxKind.MappedType; readonlyToken?: ReadonlyToken; - iterationTypeName: Identifier; - indexType: TypeNode; + typeParameter: TypeParameterDeclaration; questionToken?: QuestionToken; type?: TypeNode; } From fc450a2d2f6ee7e4e5768eb27aa921ccb5cb9f10 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Nov 2016 14:17:51 -0700 Subject: [PATCH 03/28] Introduce MappedType in type checker --- src/compiler/checker.ts | 103 ++++++++++++++++++++++++++++++++++++---- src/compiler/types.ts | 18 +++++-- 2 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9e1a0b6ec7bdb..460f0f449f58c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2246,6 +2246,9 @@ namespace ts { else if (getObjectFlags(type) & ObjectFlags.Anonymous) { writeAnonymousType(type, nextFlags); } + else if (getObjectFlags(type) & ObjectFlags.Mapped) { + writeMappedType(type); + } else if (type.flags & TypeFlags.StringOrNumberLiteral) { writer.writeStringLiteral(literalTypeToString(type)); } @@ -2536,6 +2539,32 @@ namespace ts { writePunctuation(writer, SyntaxKind.CloseBraceToken); inObjectTypeLiteral = saveInObjectTypeLiteral; } + + function writeMappedType(type: MappedType) { + const constraintType = getConstraintTypeFromMappedType(type); + if (constraintType.flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { + writePunctuation(writer, SyntaxKind.OpenBraceToken); + writer.writeLine(); + writer.increaseIndent(); + writePunctuation(writer, SyntaxKind.OpenBracketToken); + appendSymbolNameOnly((type.target || type).typeParameter.symbol, writer); + writeSpace(writer); + writeKeyword(writer, SyntaxKind.InKeyword); + writeSpace(writer); + writeType(constraintType, TypeFormatFlags.None); + writePunctuation(writer, SyntaxKind.CloseBracketToken); + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); + writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None); + writePunctuation(writer, SyntaxKind.SemicolonToken); + writer.writeLine(); + writer.decreaseIndent(); + writePunctuation(writer, SyntaxKind.CloseBraceToken); + } + else { + writeLiteralType(type, TypeFormatFlags.None); + } + } } function buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags) { @@ -4408,6 +4437,46 @@ namespace ts { } } + function forEachType(type: Type, f: (t: Type) => T): T { + return type.flags & TypeFlags.Union ? forEach((type).types, f) : f(type); + } + + // { [P in K]: T } + // Get apparent type of K + // If apparent type is a 'keyof T', get apparent type of T + // For each constituent literal type U + // create mapper from P to U + // instantiate T using mapper + // if U is string or number, create index signature with instantiated type + // otherwise create property with name from U and instantiated type + function resolveMappedTypeMembers(type: MappedType) { + const members: SymbolTable = createMap(); + let stringIndexInfo: IndexInfo; + let numberIndexInfo: IndexInfo; + const target = type.target || type; + const keyType = getApparentType(getConstraintTypeFromMappedType(type)); + const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((keyType).type)) : keyType; + forEachType(iterationType, t => { + const iterationMapper = createUnaryTypeMapper(target.typeParameter, t); + const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper; + if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) { + const propName = (t).text; + const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, propName); + prop.type = instantiateType(target.templateType, templateMapper); + members[propName] = prop; + } + }) + setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); + } + + function getConstraintTypeFromMappedType(type: MappedType) { + return instantiateType(getConstraintOfTypeParameter((type.target || type).typeParameter), type.mapper || identityMapper); + } + + function getTemplateTypeFromMappedType(type: MappedType) { + return instantiateType((type.target || type).templateType, type.mapper || identityMapper); + } + function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { if (type.flags & TypeFlags.Object) { @@ -4420,6 +4489,9 @@ namespace ts { else if ((type).objectFlags & ObjectFlags.Anonymous) { resolveAnonymousTypeMembers(type); } + else if ((type).objectFlags & ObjectFlags.Mapped) { + resolveMappedTypeMembers(type); + } } else if (type.flags & TypeFlags.Union) { resolveUnionTypeMembers(type); @@ -5834,14 +5906,16 @@ namespace ts { return links.resolvedType; } - function getTypeFromMappedTypeNode(node: MappedTypeNode) { + function getTypeFromMappedTypeNode(node: MappedTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - getTypeFromTypeNode(node.typeParameter.constraint); - if (node.type) { - getTypeFromTypeNode(node.type); - } - links.resolvedType = unknownType; + const type = createObjectType(ObjectFlags.Mapped); + type.declaration = node; + type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); + type.templateType = node.type ? getTypeFromTypeNode(node.type) : anyType; + type.aliasSymbol = aliasSymbol; + type.aliasTypeArguments = aliasTypeArguments; + links.resolvedType = type; } return links.resolvedType; } @@ -6015,7 +6089,7 @@ namespace ts { case SyntaxKind.IndexedAccessType: return getTypeFromIndexedAccessTypeNode(node); case SyntaxKind.MappedType: - return getTypeFromMappedTypeNode(node); + return getTypeFromMappedTypeNode(node, aliasSymbol, aliasTypeArguments); // This function assumes that an identifier or qualified name is a type expression // Callers should first ensure this by calling isTypeNode case SyntaxKind.Identifier: @@ -6180,7 +6254,13 @@ namespace ts { return result; } - function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): ObjectType { + function instantiateAnonymousOrMappedType(type: AnonymousType | MappedType, mapper: TypeMapper): ObjectType { + if (type.objectFlags & ObjectFlags.Instantiated) { + // If the type being instantiated is itself a instantiation, fetch the original target and + // combine the type mappers. + mapper = combineTypeMappers(type.mapper, mapper); + type = type.target; + } if (mapper.instantiations) { const cachedType = mapper.instantiations[type.id]; if (cachedType) { @@ -6191,7 +6271,7 @@ namespace ts { mapper.instantiations = []; } // Mark the anonymous type as instantiated such that our infinite instantiation detection logic can recognize it - const result = createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol); + const result = createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol); result.target = type; result.mapper = mapper; result.aliasSymbol = type.aliasSymbol; @@ -6268,7 +6348,10 @@ namespace ts { return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? - instantiateAnonymousType(type, mapper) : type; + instantiateAnonymousOrMappedType(type, mapper) : type; + } + if ((type).objectFlags & ObjectFlags.Mapped) { + return instantiateAnonymousOrMappedType(type, mapper); } if ((type).objectFlags & ObjectFlags.Reference) { return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6858d028377ba..c721770d8453b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2770,10 +2770,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 } @@ -2842,6 +2843,15 @@ namespace ts { mapper?: TypeMapper; // Instantiation mapper } + /* @internal */ + export interface MappedType extends ObjectType { + declaration: MappedTypeNode; + typeParameter: TypeParameter; + templateType: Type; + target?: MappedType; // Instantiation target + 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 From 7807ac96f58304e5a6ba979f83005b4e954055e2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Nov 2016 16:47:43 -0700 Subject: [PATCH 04/28] Attach symbols to mapped types --- src/compiler/binder.ts | 3 ++- src/compiler/checker.ts | 7 ++----- src/compiler/types.ts | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e9419ab1cc64c..4d923888e8664 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1957,9 +1957,10 @@ namespace ts { case SyntaxKind.JSDocFunctionType: return bindFunctionOrConstructorType(node); case SyntaxKind.TypeLiteral: + case SyntaxKind.MappedType: case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocRecordType: - return bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); + return bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); case SyntaxKind.ObjectLiteralExpression: return bindObjectLiteralExpression(node); case SyntaxKind.FunctionExpression: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7e87980cf1595..afd2b50c87103 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5884,10 +5884,8 @@ namespace ts { function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { if (indexType.flags & TypeFlags.TypeParameter) { - if (!isTypeAssignableTo(getConstraintOfTypeParameter(indexType) || emptyObjectType, getIndexType(objectType))) { - if (accessNode) { - error(accessNode, Diagnostics.Type_0_is_not_constrained_to_keyof_1, typeToString(indexType), typeToString(objectType)); - } + if (accessNode && !isTypeAssignableTo(getConstraintOfTypeParameter(indexType) || emptyObjectType, getIndexType(objectType))) { + error(accessNode, Diagnostics.Type_0_is_not_constrained_to_keyof_1, typeToString(indexType), typeToString(objectType)); return unknownType; } return getIndexedAccessTypeForTypeParameter(objectType, indexType); @@ -5919,7 +5917,6 @@ namespace ts { const links = getNodeLinks(node); if (!links.resolvedType) { const type = createObjectType(ObjectFlags.Mapped); - type.declaration = node; type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); type.templateType = node.type ? getTypeFromTypeNode(node.type) : anyType; type.aliasSymbol = aliasSymbol; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 36d0d3fd7b9cb..fcd5b68950b24 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2497,7 +2497,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 @@ -2848,7 +2848,6 @@ namespace ts { /* @internal */ export interface MappedType extends ObjectType { - declaration: MappedTypeNode; typeParameter: TypeParameter; templateType: Type; target?: MappedType; // Instantiation target From 1c7b397fbb512320c0eea0065fd38fdcdc121636 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Nov 2016 07:04:56 -0700 Subject: [PATCH 05/28] Introduce instantiateCached function --- src/compiler/checker.ts | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index afd2b50c87103..2a0c4eee98834 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4465,7 +4465,7 @@ namespace ts { prop.type = instantiateType(target.templateType, templateMapper); members[propName] = prop; } - }) + }); setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); } @@ -6122,6 +6122,11 @@ namespace ts { return items; } + function instantiateCached(type: T, mapper: TypeMapper, instantiator: (item: T, mapper: TypeMapper) => T): T { + const instantiations = mapper.instantiations || (mapper.instantiations = []); + return instantiations[type.id] || (instantiations[type.id] = instantiator(type, mapper)); + } + function createUnaryTypeMapper(source: Type, target: Type): TypeMapper { return t => t === source ? target : t; } @@ -6261,28 +6266,11 @@ namespace ts { } function instantiateAnonymousOrMappedType(type: AnonymousType | MappedType, mapper: TypeMapper): ObjectType { - if (type.objectFlags & ObjectFlags.Instantiated) { - // If the type being instantiated is itself a instantiation, fetch the original target and - // combine the type mappers. - mapper = combineTypeMappers(type.mapper, mapper); - type = type.target; - } - if (mapper.instantiations) { - const cachedType = mapper.instantiations[type.id]; - if (cachedType) { - return cachedType; - } - } - else { - mapper.instantiations = []; - } - // Mark the anonymous type as instantiated such that our infinite instantiation detection logic can recognize it const result = createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol); - result.target = type; - result.mapper = mapper; + result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type; + result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper; result.aliasSymbol = type.aliasSymbol; result.aliasTypeArguments = mapper.targetTypes; - mapper.instantiations[type.id] = result; return result; } @@ -6354,10 +6342,10 @@ namespace ts { return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? - instantiateAnonymousOrMappedType(type, mapper) : type; + instantiateCached(type, mapper, instantiateAnonymousOrMappedType) : type; } if ((type).objectFlags & ObjectFlags.Mapped) { - return instantiateAnonymousOrMappedType(type, mapper); + return instantiateCached(type, mapper, instantiateAnonymousOrMappedType); } if ((type).objectFlags & ObjectFlags.Reference) { return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); From 507ab30e9c4d2ab02d4caf8d033ae11226c4753a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Nov 2016 17:01:47 -0700 Subject: [PATCH 06/28] Handle readonly and optional properties + index signatures --- src/compiler/checker.ts | 57 ++++++++++++++++++++++++++++++++--------- src/compiler/types.ts | 6 +++-- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2a0c4eee98834..fe895d5be45ed 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2546,13 +2546,20 @@ namespace ts { writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); + if (type.isReadonly) { + writeKeyword(writer, SyntaxKind.ReadonlyKeyword); + writeSpace(writer); + } writePunctuation(writer, SyntaxKind.OpenBracketToken); - appendSymbolNameOnly((type.target || type).typeParameter.symbol, writer); + appendSymbolNameOnly(type.typeParameter.symbol, writer); writeSpace(writer); writeKeyword(writer, SyntaxKind.InKeyword); writeSpace(writer); writeType(constraintType, TypeFormatFlags.None); writePunctuation(writer, SyntaxKind.CloseBracketToken); + if (type.isOptional) { + writePunctuation(writer, SyntaxKind.QuestionToken); + } writePunctuation(writer, SyntaxKind.ColonToken); writeSpace(writer); writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None); @@ -4453,28 +4460,40 @@ namespace ts { const members: SymbolTable = createMap(); let stringIndexInfo: IndexInfo; let numberIndexInfo: IndexInfo; - const target = type.target || type; - const keyType = getApparentType(getConstraintTypeFromMappedType(type)); + const constraintType = getConstraintTypeFromMappedType(type); + const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType; const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((keyType).type)) : keyType; forEachType(iterationType, t => { - const iterationMapper = createUnaryTypeMapper(target.typeParameter, t); + const iterationMapper = createUnaryTypeMapper(type.typeParameter, t); const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper; + const propType = instantiateType(type.templateType, templateMapper); if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) { const propName = (t).text; - const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, propName); - prop.type = instantiateType(target.templateType, templateMapper); + const optionalFlag = type.isOptional ? SymbolFlags.Optional : 0; + const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | optionalFlag, propName); + prop.type = addOptionality(propType, type.isOptional); + prop.isReadonly = type.isReadonly; members[propName] = prop; } + else if (t.flags & TypeFlags.String) { + stringIndexInfo = createIndexInfo(propType, type.isReadonly); + } + else if (t.flags & TypeFlags.Number) { + numberIndexInfo = createIndexInfo(propType, type.isReadonly); + } }); + if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) { + numberIndexInfo = undefined; + } setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); } function getConstraintTypeFromMappedType(type: MappedType) { - return instantiateType(getConstraintOfTypeParameter((type.target || type).typeParameter), type.mapper || identityMapper); + return instantiateType(getConstraintOfTypeParameter(type.typeParameter), type.mapper || identityMapper); } function getTemplateTypeFromMappedType(type: MappedType) { - return instantiateType((type.target || type).templateType, type.mapper || identityMapper); + return instantiateType(type.templateType, type.mapper || identityMapper); } function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { @@ -5919,6 +5938,8 @@ namespace ts { const type = createObjectType(ObjectFlags.Mapped); type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); type.templateType = node.type ? getTypeFromTypeNode(node.type) : anyType; + type.isReadonly = !!node.readonlyToken; + type.isOptional = !!node.questionToken; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; links.resolvedType = type; @@ -6265,8 +6286,8 @@ namespace ts { return result; } - function instantiateAnonymousOrMappedType(type: AnonymousType | MappedType, mapper: TypeMapper): ObjectType { - const result = createObjectType(type.objectFlags | ObjectFlags.Instantiated, type.symbol); + function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper): AnonymousType { + const result = createObjectType(ObjectFlags.Anonymous | ObjectFlags.Instantiated, type.symbol); result.target = type.objectFlags & ObjectFlags.Instantiated ? type.target : type; result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper; result.aliasSymbol = type.aliasSymbol; @@ -6274,6 +6295,18 @@ namespace ts { return result; } + function instantiateMappedType(type: MappedType, mapper: TypeMapper): MappedType { + const result = createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol); + result.typeParameter = type.typeParameter; + result.templateType = type.templateType; + result.isReadonly = type.isReadonly; + result.isOptional = type.isOptional; + result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper; + result.aliasSymbol = type.aliasSymbol; + result.aliasTypeArguments = mapper.targetTypes; + return result; + } + function isSymbolInScopeOfMappedTypeParameter(symbol: Symbol, mapper: TypeMapper) { if (!(symbol.declarations && symbol.declarations.length)) { return false; @@ -6342,10 +6375,10 @@ namespace ts { return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? - instantiateCached(type, mapper, instantiateAnonymousOrMappedType) : type; + instantiateCached(type, mapper, instantiateAnonymousType) : type; } if ((type).objectFlags & ObjectFlags.Mapped) { - return instantiateCached(type, mapper, instantiateAnonymousOrMappedType); + return instantiateCached(type, mapper, instantiateMappedType); } if ((type).objectFlags & ObjectFlags.Reference) { return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fcd5b68950b24..7bea81ef6b622 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -899,7 +899,7 @@ namespace ts { indexType: TypeNode; } - export interface MappedTypeNode extends TypeNode { + export interface MappedTypeNode extends TypeNode, Declaration { kind: SyntaxKind.MappedType; readonlyToken?: ReadonlyToken; typeParameter: TypeParameterDeclaration; @@ -2850,7 +2850,9 @@ namespace ts { export interface MappedType extends ObjectType { typeParameter: TypeParameter; templateType: Type; - target?: MappedType; // Instantiation target + isReadonly: boolean; + isOptional: boolean; + // target?: MappedType; // Instantiation target mapper?: TypeMapper; // Instantiation mapper } From 2564e1cd26428d3ec36d9db5b3c4aeb6800e2976 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 6 Nov 2016 12:38:49 -0800 Subject: [PATCH 07/28] Handle recursion in mapped type display --- src/compiler/checker.ts | 62 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fe895d5be45ed..5c171341e6c5e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2243,12 +2243,9 @@ namespace ts { else if (type.flags & TypeFlags.UnionOrIntersection) { writeUnionOrIntersectionType(type, nextFlags); } - else if (getObjectFlags(type) & ObjectFlags.Anonymous) { + else if (getObjectFlags(type) & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { writeAnonymousType(type, nextFlags); } - else if (getObjectFlags(type) & ObjectFlags.Mapped) { - writeMappedType(type); - } else if (type.flags & TypeFlags.StringOrNumberLiteral) { writer.writeStringLiteral(literalTypeToString(type)); } @@ -2465,6 +2462,13 @@ namespace ts { } function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) { + if (type.objectFlags & ObjectFlags.Mapped) { + if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { + writeMappedType(type); + return; + } + } + const resolved = resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { @@ -2541,36 +2545,30 @@ namespace ts { } function writeMappedType(type: MappedType) { - const constraintType = getConstraintTypeFromMappedType(type); - if (constraintType.flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { - writePunctuation(writer, SyntaxKind.OpenBraceToken); - writer.writeLine(); - writer.increaseIndent(); - if (type.isReadonly) { - writeKeyword(writer, SyntaxKind.ReadonlyKeyword); - writeSpace(writer); - } - writePunctuation(writer, SyntaxKind.OpenBracketToken); - appendSymbolNameOnly(type.typeParameter.symbol, writer); - writeSpace(writer); - writeKeyword(writer, SyntaxKind.InKeyword); - writeSpace(writer); - writeType(constraintType, TypeFormatFlags.None); - writePunctuation(writer, SyntaxKind.CloseBracketToken); - if (type.isOptional) { - writePunctuation(writer, SyntaxKind.QuestionToken); - } - writePunctuation(writer, SyntaxKind.ColonToken); + writePunctuation(writer, SyntaxKind.OpenBraceToken); + writer.writeLine(); + writer.increaseIndent(); + if (type.isReadonly) { + writeKeyword(writer, SyntaxKind.ReadonlyKeyword); writeSpace(writer); - writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None); - writePunctuation(writer, SyntaxKind.SemicolonToken); - writer.writeLine(); - writer.decreaseIndent(); - writePunctuation(writer, SyntaxKind.CloseBraceToken); } - else { - writeLiteralType(type, TypeFormatFlags.None); + writePunctuation(writer, SyntaxKind.OpenBracketToken); + appendSymbolNameOnly(type.typeParameter.symbol, writer); + writeSpace(writer); + writeKeyword(writer, SyntaxKind.InKeyword); + writeSpace(writer); + writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None); + writePunctuation(writer, SyntaxKind.CloseBracketToken); + if (type.isOptional) { + writePunctuation(writer, SyntaxKind.QuestionToken); } + writePunctuation(writer, SyntaxKind.ColonToken); + writeSpace(writer); + writeType(getTemplateTypeFromMappedType(type), TypeFormatFlags.None); + writePunctuation(writer, SyntaxKind.SemicolonToken); + writer.writeLine(); + writer.decreaseIndent(); + writePunctuation(writer, SyntaxKind.CloseBraceToken); } } @@ -5935,7 +5933,7 @@ namespace ts { function getTypeFromMappedTypeNode(node: MappedTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - const type = createObjectType(ObjectFlags.Mapped); + const type = createObjectType(ObjectFlags.Mapped, node.symbol); type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); type.templateType = node.type ? getTypeFromTypeNode(node.type) : anyType; type.isReadonly = !!node.readonlyToken; From 5de63a74eb896265a9c948080d5dd8fc4960877f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 7 Nov 2016 09:43:01 -0800 Subject: [PATCH 08/28] Validate constraint type in mapped type --- src/compiler/checker.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5c171341e6c5e..94d5a393fea12 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5933,14 +5933,22 @@ namespace ts { function getTypeFromMappedTypeNode(node: MappedTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - const type = createObjectType(ObjectFlags.Mapped, node.symbol); - type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); - type.templateType = node.type ? getTypeFromTypeNode(node.type) : anyType; - type.isReadonly = !!node.readonlyToken; - type.isOptional = !!node.questionToken; - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; - links.resolvedType = type; + const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); + const constraintType = getConstraintOfTypeParameter(typeParameter); + const keyType = constraintType && constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType; + if (keyType && (keyType.flags & TypeFlags.Index || checkTypeAssignableTo(keyType, stringOrNumberType, node.typeParameter.constraint))) { + const type = createObjectType(ObjectFlags.Mapped, node.symbol); + type.typeParameter = typeParameter; + type.templateType = node.type ? getTypeFromTypeNode(node.type) : unknownType; + type.isReadonly = !!node.readonlyToken; + type.isOptional = !!node.questionToken; + type.aliasSymbol = aliasSymbol; + type.aliasTypeArguments = aliasTypeArguments; + links.resolvedType = type; + } + else { + links.resolvedType = unknownType; + } } return links.resolvedType; } From de93876abe412c89f0f114c2b31f7c715c8652bf Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 7 Nov 2016 09:43:20 -0800 Subject: [PATCH 09/28] Correct symbol display for type parameter of mapped type --- src/services/symbolDisplay.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 516b5d7fbc5b1..b32bd8f331d4f 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -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); } @@ -288,6 +286,7 @@ namespace ts.SymbolDisplay { if (declaration) { if (isFunctionLikeKind(declaration.kind)) { + addInPrefix(); const signature = typeChecker.getSignatureFromDeclaration(declaration); if (declaration.kind === SyntaxKind.ConstructSignature) { displayParts.push(keywordPart(SyntaxKind.NewKeyword)); @@ -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[]; // Both T will go through same code path + addInPrefix(); displayParts.push(keywordPart(SyntaxKind.TypeKeyword)); displayParts.push(spacePart()); addFullSymbolName(declaration.symbol); @@ -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); From 9f3aa38d17976ba54ffcc3f7a4331d499be18b94 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 7 Nov 2016 10:24:54 -0800 Subject: [PATCH 10/28] Improve sharing by re-instantiating top level type aliases --- src/compiler/checker.ts | 93 ++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 94d5a393fea12..c0228a97eafbc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5259,21 +5259,27 @@ namespace ts { return type; } + function getTypeAliasInstantiation(symbol: Symbol, typeArguments: Type[]): Type { + const type = getDeclaredTypeOfSymbol(symbol); + const links = getSymbolLinks(symbol); + const typeParameters = links.typeParameters; + const id = getTypeListId(typeArguments); + return links.instantiations[id] || (links.instantiations[id] = instantiateTypeNoAlias(type, createTypeMapper(typeParameters, typeArguments))); + } + // Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include // references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the // declared type. Instantiations are cached using the type identities of the type arguments as the key. function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { const type = getDeclaredTypeOfSymbol(symbol); - const links = getSymbolLinks(symbol); - const typeParameters = links.typeParameters; + const typeParameters = getSymbolLinks(symbol).typeParameters; if (typeParameters) { if (!node.typeArguments || node.typeArguments.length !== typeParameters.length) { error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, symbolToString(symbol), typeParameters.length); return unknownType; } const typeArguments = map(node.typeArguments, getTypeFromTypeNodeNoAlias); - const id = getTypeListId(typeArguments); - return links.instantiations[id] || (links.instantiations[id] = instantiateType(type, createTypeMapper(typeParameters, typeArguments))); + return getTypeAliasInstantiation(symbol, typeArguments); } if (node.typeArguments) { error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol)); @@ -6365,44 +6371,63 @@ namespace ts { return false; } + function isTopLevelTypeAlias(symbol: Symbol) { + if (symbol.declarations && symbol.declarations.length) { + const declaration = symbol.declarations[0]; + return declaration.kind === SyntaxKind.SourceFile || declaration.kind === SyntaxKind.ModuleBlock; + } + return false; + } + function instantiateType(type: Type, mapper: TypeMapper): Type { if (type && mapper !== identityMapper) { - if (type.flags & TypeFlags.TypeParameter) { - return mapper(type); - } - if (type.flags & TypeFlags.Object) { - if ((type).objectFlags & ObjectFlags.Anonymous) { - // If the anonymous type originates in a declaration of a function, method, class, or - // interface, in an object type literal, or in an object literal expression, we may need - // to instantiate the type because it might reference a type parameter. We skip instantiation - // if none of the type parameters that are in scope in the type's declaration are mapped by - // the given mapper, however we can only do that analysis if the type isn't itself an - // instantiation. - return type.symbol && - type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && - ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? - instantiateCached(type, mapper, instantiateAnonymousType) : type; - } - if ((type).objectFlags & ObjectFlags.Mapped) { - return instantiateCached(type, mapper, instantiateMappedType); + if (type.aliasSymbol && isTopLevelTypeAlias(type.aliasSymbol)) { + if (type.aliasTypeArguments) { + return getTypeAliasInstantiation(type.aliasSymbol, instantiateList(type.aliasTypeArguments, mapper, instantiateType)); } - if ((type).objectFlags & ObjectFlags.Reference) { - return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); - } - } - if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { - return getUnionType(instantiateList((type).types, mapper, instantiateType), /*subtypeReduction*/ false, type.aliasSymbol, mapper.targetTypes); + return type; } - if (type.flags & TypeFlags.Intersection) { - return getIntersectionType(instantiateList((type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes); + return instantiateTypeNoAlias(type, mapper); + } + return type; + } + + function instantiateTypeNoAlias(type: Type, mapper: TypeMapper): Type { + if (type.flags & TypeFlags.TypeParameter) { + return mapper(type); + } + if (type.flags & TypeFlags.Object) { + if ((type).objectFlags & ObjectFlags.Anonymous) { + // If the anonymous type originates in a declaration of a function, method, class, or + // interface, in an object type literal, or in an object literal expression, we may need + // to instantiate the type because it might reference a type parameter. We skip instantiation + // if none of the type parameters that are in scope in the type's declaration are mapped by + // the given mapper, however we can only do that analysis if the type isn't itself an + // instantiation. + return type.symbol && + type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && + ((type).objectFlags & ObjectFlags.Instantiated || isSymbolInScopeOfMappedTypeParameter(type.symbol, mapper)) ? + instantiateCached(type, mapper, instantiateAnonymousType) : type; } - if (type.flags & TypeFlags.Index) { - return getIndexType(instantiateType((type).type, mapper)); + if ((type).objectFlags & ObjectFlags.Mapped) { + return instantiateCached(type, mapper, instantiateMappedType); } - if (type.flags & TypeFlags.IndexedAccess) { - return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper)); + if ((type).objectFlags & ObjectFlags.Reference) { + return createTypeReference((type).target, instantiateList((type).typeArguments, mapper, instantiateType)); } } + if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) { + return getUnionType(instantiateList((type).types, mapper, instantiateType), /*subtypeReduction*/ false, type.aliasSymbol, mapper.targetTypes); + } + if (type.flags & TypeFlags.Intersection) { + return getIntersectionType(instantiateList((type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes); + } + if (type.flags & TypeFlags.Index) { + return getIndexType(instantiateType((type).type, mapper)); + } + if (type.flags & TypeFlags.IndexedAccess) { + return getIndexedAccessType(instantiateType((type).objectType, mapper), instantiateType((type).indexType, mapper)); + } return type; } From 8aef1e6bb14a05401468f9107245615cc8c2dd2b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 8 Nov 2016 06:55:35 -0800 Subject: [PATCH 11/28] Type inference for mapped types --- src/compiler/checker.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c0228a97eafbc..8349f985f586e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5941,7 +5941,7 @@ namespace ts { if (!links.resolvedType) { const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); const constraintType = getConstraintOfTypeParameter(typeParameter); - const keyType = constraintType && constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType; + const keyType = constraintType && constraintType.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(constraintType) : constraintType; if (keyType && (keyType.flags & TypeFlags.Index || checkTypeAssignableTo(keyType, stringOrNumberType, node.typeParameter.constraint))) { const type = createObjectType(ObjectFlags.Mapped, node.symbol); type.typeParameter = typeParameter; @@ -8117,9 +8117,11 @@ namespace ts { // we perform type inference (i.e. a type parameter of a generic function). We cache // results for union and intersection types for performance reasons. function couldContainTypeParameters(type: Type): boolean { + const objectFlags = getObjectFlags(type); return !!(type.flags & TypeFlags.TypeParameter || - getObjectFlags(type) & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) || - getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || + objectFlags & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) || + objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || + objectFlags & ObjectFlags.Mapped || type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(type)); } @@ -8267,6 +8269,19 @@ namespace ts { } } else { + if (getObjectFlags(target) & ObjectFlags.Mapped) { + const constraintType = getConstraintTypeFromMappedType(target); + if (getObjectFlags(source) & ObjectFlags.Mapped) { + inferFromTypes(getConstraintTypeFromMappedType(source), constraintType); + inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); + return; + } + if (constraintType.flags & TypeFlags.TypeParameter) { + inferFromTypes(getIndexType(source), constraintType); + inferFromTypes(getUnionType(map(getPropertiesOfType(source), getTypeOfSymbol)), getTemplateTypeFromMappedType(target)); + return; + } + } source = getApparentType(source); if (source.flags & TypeFlags.Object) { if (isInProcess(source, target)) { From a562d6e63ff221763e3dca6e133100c4b3b8ffad Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 8 Nov 2016 10:14:44 -0800 Subject: [PATCH 12/28] Make keyof T assignable to and subtype of string | number --- src/compiler/checker.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index af7dae030dbe5..3b17c06c0e870 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6917,6 +6917,13 @@ namespace ts { if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; + if (source.flags & TypeFlags.Index) { + // A keyof T is related to a union type containing both string and number + if (maybeTypeOfKind(target, TypeFlags.String) && maybeTypeOfKind(target, TypeFlags.Number)) { + return Ternary.True; + } + } + if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { if (hasExcessProperties(source, target, reportErrors)) { if (reportErrors) { From cf2953bc44dd99b51fdc15c4eb084d06f0f07d0a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 8 Nov 2016 12:21:44 -0800 Subject: [PATCH 13/28] Add relations for keyof S / keyof T and [P in S]: X / [P in T]: X --- src/compiler/checker.ts | 50 ++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3b17c06c0e870..d2d2e9f84d993 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2229,7 +2229,7 @@ namespace ts { // The specified symbol flags need to be reinterpreted as type flags buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags); } - else if (!(flags & TypeFormatFlags.InTypeAlias) && (getObjectFlags(type) & ObjectFlags.Anonymous || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol && + else if (!(flags & TypeFormatFlags.InTypeAlias) && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const typeArguments = type.aliasTypeArguments; writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags); @@ -4488,6 +4488,14 @@ namespace ts { return instantiateType(type.templateType, type.mapper || identityMapper); } + function isGenericMappedType(type: Type) { + if (getObjectFlags(type) & ObjectFlags.Mapped) { + const constraintType = getConstraintTypeFromMappedType(type); + return !!(constraintType.flags & (TypeFlags.TypeParameter | TypeFlags.Index)); + } + return false; + } + function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type).members) { if (type.flags & TypeFlags.Object) { @@ -6996,6 +7004,12 @@ namespace ts { } } else if (target.flags & TypeFlags.Index) { + // A keyof S is related to a keyof T if T is related to S. + if (source.flags & TypeFlags.Index) { + if (result = isRelatedTo((target).type, (source).type, /*reportErrors*/ false)) { + return result; + } + } // Given a type parameter T with a constraint C, a type S is assignable to // keyof T if S is assignable to keyof C. const constraint = getConstraintOfTypeParameter((target).type); @@ -7030,18 +7044,28 @@ namespace ts { return result; } } - // Even if relationship doesn't hold for unions, intersections, or generic type references, - // it may hold in a structural comparison. - const apparentSource = getApparentType(source); - // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates - // to X. Failing both of those we want to check if the aggregation of A and B's members structurally - // relates to X. Thus, we include intersection types on the source side here. - if (apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { - // Report structural errors only if we haven't reported any errors yet - const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & TypeFlags.Primitive); - if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) { - errorInfo = saveErrorInfo; - return result; + else if (isGenericMappedType(target)) { + // A type [P in S]: X is related to a type [P in T]: X if T is related to S. + if (isGenericMappedType(source) && + isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), /*reportErrors*/ false) && + isTypeIdenticalTo(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target))) { + return Ternary.True; + } + } + else { + // Even if relationship doesn't hold for unions, intersections, or generic type references, + // it may hold in a structural comparison. + const apparentSource = getApparentType(source); + // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates + // to X. Failing both of those we want to check if the aggregation of A and B's members structurally + // relates to X. Thus, we include intersection types on the source side here. + if (apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { + // Report structural errors only if we haven't reported any errors yet + const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo && !(source.flags & TypeFlags.Primitive); + if (result = objectTypeRelatedTo(apparentSource, source, target, reportStructuralErrors)) { + errorInfo = saveErrorInfo; + return result; + } } } } From aca7e2fa3bcb6b1ebe623e79578aaa7faf98a8b6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 8 Nov 2016 13:38:52 -0800 Subject: [PATCH 14/28] Don't include private/protected properties in keyof T --- src/compiler/checker.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d2d2e9f84d993..8ed8df17c68a0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5816,7 +5816,9 @@ namespace ts { } function getLiteralTypeFromPropertyName(prop: Symbol) { - return startsWith(prop.name, "__@") ? neverType : getLiteralTypeForText(TypeFlags.StringLiteral, unescapeIdentifier(prop.name)); + return getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier || startsWith(prop.name, "__@") ? + neverType : + getLiteralTypeForText(TypeFlags.StringLiteral, unescapeIdentifier(prop.name)); } function getLiteralTypeFromPropertyNames(type: Type) { @@ -7044,7 +7046,7 @@ namespace ts { return result; } } - else if (isGenericMappedType(target)) { + if (isGenericMappedType(target)) { // A type [P in S]: X is related to a type [P in T]: X if T is related to S. if (isGenericMappedType(source) && isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), /*reportErrors*/ false) && From 3dd11e48a9fda2e4ee8d4efb2698eb9f92814980 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 9 Nov 2016 09:59:15 -0800 Subject: [PATCH 15/28] Properly implement type relationship for '[P in S]: X' and '[P in T]: Y' --- src/compiler/checker.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8ed8df17c68a0..cfabcd3f5ba24 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7047,11 +7047,12 @@ namespace ts { } } if (isGenericMappedType(target)) { - // A type [P in S]: X is related to a type [P in T]: X if T is related to S. - if (isGenericMappedType(source) && - isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), /*reportErrors*/ false) && - isTypeIdenticalTo(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target))) { - return Ternary.True; + // A type [P in S]: X is related to a type [P in T]: Y if T is related to S and X is related to Y. + if (isGenericMappedType(source)) { + if ((result = isRelatedTo(getConstraintTypeFromMappedType(target), getConstraintTypeFromMappedType(source), reportErrors)) && + (result = isRelatedTo(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target), reportErrors))) { + return result; + } } } else { From 2170ff6f16005ff03deb0f19806095c6c5a68c8d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 10 Nov 2016 09:11:21 -0800 Subject: [PATCH 16/28] Defer resolution of mapped types to enable recursive definitions --- src/compiler/checker.ts | 76 +++++++++++++++++++++-------------------- src/compiler/types.ts | 9 +++-- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cfabcd3f5ba24..14e01cbc3a2ca 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2542,18 +2542,18 @@ namespace ts { writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); - if (type.isReadonly) { + if (type.declaration.readonlyToken) { writeKeyword(writer, SyntaxKind.ReadonlyKeyword); writeSpace(writer); } writePunctuation(writer, SyntaxKind.OpenBracketToken); - appendSymbolNameOnly(type.typeParameter.symbol, writer); + appendSymbolNameOnly(getTypeParameterFromMappedType(type).symbol, writer); writeSpace(writer); writeKeyword(writer, SyntaxKind.InKeyword); writeSpace(writer); writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None); writePunctuation(writer, SyntaxKind.CloseBracketToken); - if (type.isOptional) { + if (type.declaration.questionToken) { writePunctuation(writer, SyntaxKind.QuestionToken); } writePunctuation(writer, SyntaxKind.ColonToken); @@ -4452,26 +4452,29 @@ namespace ts { const members: SymbolTable = createMap(); let stringIndexInfo: IndexInfo; let numberIndexInfo: IndexInfo; + const typeParameter = getTypeParameterFromMappedType(type); const constraintType = getConstraintTypeFromMappedType(type); + const templateType = getTemplateTypeFromMappedType(type); + const isReadonly = !!type.declaration.readonlyToken; + const isOptional = !!type.declaration.questionToken; const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType; const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((keyType).type)) : keyType; forEachType(iterationType, t => { - const iterationMapper = createUnaryTypeMapper(type.typeParameter, t); + const iterationMapper = createUnaryTypeMapper(typeParameter, t); const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper; - const propType = instantiateType(type.templateType, templateMapper); + const propType = instantiateType(templateType, templateMapper); if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) { const propName = (t).text; - const optionalFlag = type.isOptional ? SymbolFlags.Optional : 0; - const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | optionalFlag, propName); - prop.type = addOptionality(propType, type.isOptional); - prop.isReadonly = type.isReadonly; + const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName); + prop.type = addOptionality(propType, isOptional); + prop.isReadonly = isReadonly; members[propName] = prop; } else if (t.flags & TypeFlags.String) { - stringIndexInfo = createIndexInfo(propType, type.isReadonly); + stringIndexInfo = createIndexInfo(propType, isReadonly); } else if (t.flags & TypeFlags.Number) { - numberIndexInfo = createIndexInfo(propType, type.isReadonly); + numberIndexInfo = createIndexInfo(propType, isReadonly); } }); if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) { @@ -4480,12 +4483,21 @@ namespace ts { setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); } + function getTypeParameterFromMappedType(type: MappedType) { + return type.typeParameter || + (type.typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(type.declaration.typeParameter))); + } + function getConstraintTypeFromMappedType(type: MappedType) { - return instantiateType(getConstraintOfTypeParameter(type.typeParameter), type.mapper || identityMapper); + return type.constraintType || + (type.constraintType = instantiateType(getConstraintOfTypeParameter(getTypeParameterFromMappedType(type)), type.mapper || identityMapper) || unknownType); } function getTemplateTypeFromMappedType(type: MappedType) { - return instantiateType(type.templateType, type.mapper || identityMapper); + return type.templateType || + (type.templateType = type.declaration.type ? + instantiateType(getTypeFromTypeNode(type.declaration.type), type.mapper || identityMapper) : + unknownType); } function isGenericMappedType(type: Type) { @@ -5949,22 +5961,11 @@ namespace ts { function getTypeFromMappedTypeNode(node: MappedTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfNode(node.typeParameter)); - const constraintType = getConstraintOfTypeParameter(typeParameter); - const keyType = constraintType && constraintType.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(constraintType) : constraintType; - if (keyType && (keyType.flags & TypeFlags.Index || checkTypeAssignableTo(keyType, stringOrNumberType, node.typeParameter.constraint))) { - const type = createObjectType(ObjectFlags.Mapped, node.symbol); - type.typeParameter = typeParameter; - type.templateType = node.type ? getTypeFromTypeNode(node.type) : unknownType; - type.isReadonly = !!node.readonlyToken; - type.isOptional = !!node.questionToken; - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; - links.resolvedType = type; - } - else { - links.resolvedType = unknownType; - } + const type = createObjectType(ObjectFlags.Mapped, node.symbol); + type.declaration = node; + type.aliasSymbol = aliasSymbol; + type.aliasTypeArguments = aliasTypeArguments; + links.resolvedType = type; } return links.resolvedType; } @@ -6326,11 +6327,8 @@ namespace ts { function instantiateMappedType(type: MappedType, mapper: TypeMapper): MappedType { const result = createObjectType(ObjectFlags.Mapped | ObjectFlags.Instantiated, type.symbol); - result.typeParameter = type.typeParameter; - result.templateType = type.templateType; - result.isReadonly = type.isReadonly; - result.isOptional = type.isOptional; - result.mapper = type.objectFlags & ObjectFlags.Instantiated ? combineTypeMappers(type.mapper, mapper) : mapper; + result.declaration = type.declaration; + result.mapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper; result.aliasSymbol = type.aliasSymbol; result.aliasTypeArguments = instantiateTypes(type.aliasTypeArguments, mapper); return result; @@ -6390,8 +6388,8 @@ namespace ts { function isTopLevelTypeAlias(symbol: Symbol) { if (symbol.declarations && symbol.declarations.length) { - const declaration = symbol.declarations[0]; - return declaration.kind === SyntaxKind.SourceFile || declaration.kind === SyntaxKind.ModuleBlock; + const parentKind = symbol.declarations[0].parent.kind; + return parentKind === SyntaxKind.SourceFile || parentKind === SyntaxKind.ModuleBlock; } return false; } @@ -6400,7 +6398,7 @@ namespace ts { if (type && mapper !== identityMapper) { if (type.aliasSymbol && isTopLevelTypeAlias(type.aliasSymbol)) { if (type.aliasTypeArguments) { - return getTypeAliasInstantiation(type.aliasSymbol, instantiateList(type.aliasTypeArguments, mapper, instantiateType)); + return getTypeAliasInstantiation(type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)); } return type; } @@ -15311,6 +15309,10 @@ namespace ts { function checkMappedType(node: MappedTypeNode) { getTypeFromMappedTypeNode(node); + // const type = getTypeFromMappedTypeNode(node); + // const constraintType = getConstraintTypeFromMappedType(type); + // const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(constraintType) : constraintType; + // checkTypeAssignableTo(keyType, stringOrNumberType, node.typeParameter.constraint); } function isPrivateWithinAmbient(node: Node): boolean { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 99d776650b4e2..13b313f16891b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2849,11 +2849,10 @@ namespace ts { /* @internal */ export interface MappedType extends ObjectType { - typeParameter: TypeParameter; - templateType: Type; - isReadonly: boolean; - isOptional: boolean; - // target?: MappedType; // Instantiation target + declaration: MappedTypeNode; + typeParameter?: TypeParameter; + constraintType?: Type; + templateType?: Type; mapper?: TypeMapper; // Instantiation mapper } From b81c226639c88be0c26c44c951445b32db1757eb Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 10 Nov 2016 10:31:24 -0800 Subject: [PATCH 17/28] Use pull model to obtain type alias information for type nodes --- src/compiler/checker.ts | 78 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 14e01cbc3a2ca..8f7d8aa0d6126 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3685,7 +3685,7 @@ namespace ts { function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[]): Signature[] { let signatures = getConstructorsForTypeArguments(type, typeArgumentNodes); if (typeArgumentNodes) { - const typeArguments = map(typeArgumentNodes, getTypeFromTypeNodeNoAlias); + const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode); signatures = map(signatures, sig => getSignatureInstantiation(sig, typeArguments)); } return signatures; @@ -3896,7 +3896,6 @@ namespace ts { return unknownType; } - const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); let type: Type; if (declaration) { @@ -3909,16 +3908,17 @@ namespace ts { } else { declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); - type = getTypeFromTypeNode(declaration.type, symbol, typeParameters); + type = getTypeFromTypeNode(declaration.type); } if (popTypeResolution()) { - links.typeParameters = typeParameters; + const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); if (typeParameters) { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. + links.typeParameters = typeParameters; links.instantiations = createMap(); - links.instantiations[getTypeListId(links.typeParameters)] = type; + links.instantiations[getTypeListId(typeParameters)] = type; } } else { @@ -4254,7 +4254,7 @@ namespace ts { return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false)]; } const baseTypeNode = getBaseTypeNodeOfClass(classType); - const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNodeNoAlias); + const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode); const typeArgCount = typeArguments ? typeArguments.length : 0; const result: Signature[] = []; for (const baseSig of baseSignatures) { @@ -5270,7 +5270,7 @@ namespace ts { // In a type reference, the outer type parameters of the referenced class or interface are automatically // supplied as type arguments and the type reference only specifies arguments for the local type parameters // of the class or interface. - return createTypeReference(type, concatenate(type.outerTypeParameters, map(node.typeArguments, getTypeFromTypeNodeNoAlias))); + return createTypeReference(type, concatenate(type.outerTypeParameters, map(node.typeArguments, getTypeFromTypeNode))); } if (node.typeArguments) { error(node, Diagnostics.Type_0_is_not_generic, typeToString(type)); @@ -5298,7 +5298,7 @@ namespace ts { error(node, Diagnostics.Generic_type_0_requires_1_type_argument_s, symbolToString(symbol), typeParameters.length); return unknownType; } - const typeArguments = map(node.typeArguments, getTypeFromTypeNodeNoAlias); + const typeArguments = map(node.typeArguments, getTypeFromTypeNode); return getTypeAliasInstantiation(symbol, typeArguments); } if (node.typeArguments) { @@ -5550,7 +5550,7 @@ namespace ts { function getTypeFromTupleTypeNode(node: TupleTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNodeNoAlias)); + links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode)); } return links.resolvedType; } @@ -5740,10 +5740,11 @@ namespace ts { return type; } - function getTypeFromUnionTypeNode(node: UnionTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + function getTypeFromUnionTypeNode(node: UnionTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNodeNoAlias), /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); + links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*subtypeReduction*/ false, + getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node)); } return links.resolvedType; } @@ -5811,10 +5812,11 @@ namespace ts { return type; } - function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNodeNoAlias), aliasSymbol, aliasTypeArguments); + links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode), + getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node)); } return links.resolvedType; } @@ -5847,7 +5849,7 @@ namespace ts { function getTypeFromTypeOperatorNode(node: TypeOperatorNode) { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getIndexType(getTypeFromTypeNodeNoAlias(node.type)); + links.resolvedType = getIndexType(getTypeFromTypeNode(node.type)); } return links.resolvedType; } @@ -5953,40 +5955,50 @@ namespace ts { function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getIndexedAccessType(getTypeFromTypeNodeNoAlias(node.objectType), getTypeFromTypeNodeNoAlias(node.indexType), node); + links.resolvedType = getIndexedAccessType(getTypeFromTypeNode(node.objectType), getTypeFromTypeNode(node.indexType), node); } return links.resolvedType; } - function getTypeFromMappedTypeNode(node: MappedTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + function getTypeFromMappedTypeNode(node: MappedTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { const type = createObjectType(ObjectFlags.Mapped, node.symbol); type.declaration = node; - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; + type.aliasSymbol = getAliasSymbolForTypeNode(node); + type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node); links.resolvedType = type; } return links.resolvedType; } - function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: TypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { // Deferred resolution of members is handled by resolveObjectTypeMembers - if (isEmpty(node.symbol.members) && !aliasSymbol && !aliasTypeArguments) { + const aliasSymbol = getAliasSymbolForTypeNode(node); + if (isEmpty(node.symbol.members) && !aliasSymbol) { links.resolvedType = emptyTypeLiteralType; } else { const type = createObjectType(ObjectFlags.Anonymous, node.symbol); type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; + type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node); links.resolvedType = type; } } return links.resolvedType; } + function getAliasSymbolForTypeNode(node: TypeNode) { + return node.parent.kind === SyntaxKind.TypeAliasDeclaration ? getSymbolOfNode(node.parent) : undefined; + } + + function getAliasTypeArgumentsForTypeNode(node: TypeNode) { + const symbol = getAliasSymbolForTypeNode(node); + return symbol ? getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) : undefined; + } + function createLiteralType(flags: TypeFlags, text: string) { const type = createType(flags); type.text = text; @@ -6034,7 +6046,7 @@ namespace ts { function getTypeFromJSDocTupleType(node: JSDocTupleType): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - const types = map(node.types, getTypeFromTypeNodeNoAlias); + const types = map(node.types, getTypeFromTypeNode); links.resolvedType = createTupleType(types); } return links.resolvedType; @@ -6061,11 +6073,7 @@ namespace ts { return links.resolvedType; } - function getTypeFromTypeNodeNoAlias(type: TypeNode) { - return getTypeFromTypeNode(type, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined); - } - - function getTypeFromTypeNode(node: TypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + function getTypeFromTypeNode(node: TypeNode): Type { switch (node.kind) { case SyntaxKind.AnyKeyword: case SyntaxKind.JSDocAllType: @@ -6116,9 +6124,9 @@ namespace ts { return getTypeFromTupleTypeNode(node); case SyntaxKind.UnionType: case SyntaxKind.JSDocUnionType: - return getTypeFromUnionTypeNode(node, aliasSymbol, aliasTypeArguments); + return getTypeFromUnionTypeNode(node); case SyntaxKind.IntersectionType: - return getTypeFromIntersectionTypeNode(node, aliasSymbol, aliasTypeArguments); + return getTypeFromIntersectionTypeNode(node); case SyntaxKind.ParenthesizedType: case SyntaxKind.JSDocNullableType: case SyntaxKind.JSDocNonNullableType: @@ -6133,13 +6141,13 @@ namespace ts { case SyntaxKind.TypeLiteral: case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocFunctionType: - return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments); + return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); case SyntaxKind.TypeOperator: return getTypeFromTypeOperatorNode(node); case SyntaxKind.IndexedAccessType: return getTypeFromIndexedAccessTypeNode(node); case SyntaxKind.MappedType: - return getTypeFromMappedTypeNode(node, aliasSymbol, aliasTypeArguments); + return getTypeFromMappedTypeNode(node); // This function assumes that an identifier or qualified name is a type expression // Callers should first ensure this by calling isTypeNode case SyntaxKind.Identifier: @@ -12709,7 +12717,7 @@ namespace ts { else if (candidateForTypeArgumentError) { if (!isTaggedTemplate && !isDecorator && typeArguments) { const typeArguments = (node).typeArguments; - checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, getTypeFromTypeNodeNoAlias), /*reportErrors*/ true, headMessage); + checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, getTypeFromTypeNode), /*reportErrors*/ true, headMessage); } else { Debug.assert(resultOfFailedInference.failedTypeParameterIndex >= 0); @@ -12740,7 +12748,7 @@ namespace ts { for (let candidate of candidates) { if (hasCorrectArity(node, args, candidate)) { if (candidate.typeParameters && typeArguments) { - candidate = getSignatureInstantiation(candidate, map(typeArguments, getTypeFromTypeNodeNoAlias)); + candidate = getSignatureInstantiation(candidate, map(typeArguments, getTypeFromTypeNode)); } return candidate; } @@ -12776,7 +12784,7 @@ namespace ts { if (candidate.typeParameters) { let typeArgumentTypes: Type[]; if (typeArguments) { - typeArgumentTypes = map(typeArguments, getTypeFromTypeNodeNoAlias); + typeArgumentTypes = map(typeArguments, getTypeFromTypeNode); typeArgumentsAreValid = checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false); } else { @@ -15238,7 +15246,7 @@ namespace ts { const constraint = getConstraintOfTypeParameter(typeParameters[i]); if (constraint) { if (!typeArguments) { - typeArguments = map(typeArgumentNodes, getTypeFromTypeNodeNoAlias); + typeArguments = map(typeArgumentNodes, getTypeFromTypeNode); mapper = createTypeMapper(typeParameters, typeArguments); } const typeArgument = typeArguments[i]; From de2da2cb72b41ee8b52378be7e01ec491d95ffc2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 10 Nov 2016 10:32:05 -0800 Subject: [PATCH 18/28] Accept new baselines --- ...structuringParameterProperties5.errors.txt | 24 +++++++++---------- .../reference/implicitIndexSignatures.types | 16 ++++++------- .../intersectionTypeNormalization.types | 12 +++++----- .../reference/keyofAndIndexedAccess.types | 12 +++++----- .../reference/noErrorTruncation.errors.txt | 4 ++-- .../nounusedTypeParameterConstraint.types | 2 +- .../reference/typeAliasDeclarationEmit2.types | 2 +- tests/baselines/reference/typeAliases.types | 8 +++---- ...lyReferencedTypeAliasToTypeLiteral02.types | 18 +++++++------- ...ypeGuardNarrowsPrimitiveIntersection.types | 18 +++++++------- 10 files changed, 58 insertions(+), 58 deletions(-) diff --git a/tests/baselines/reference/destructuringParameterProperties5.errors.txt b/tests/baselines/reference/destructuringParameterProperties5.errors.txt index 8eff6ad0febee..e0540240b61c4 100644 --- a/tests/baselines/reference/destructuringParameterProperties5.errors.txt +++ b/tests/baselines/reference/destructuringParameterProperties5.errors.txt @@ -1,15 +1,15 @@ tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(5,17): error TS1187: A parameter property may not be declared using a binding pattern. -tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(5,27): error TS2459: Type '{ x: number; y: string; z: boolean; }' has no property 'x1' and no string index signature. -tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(5,31): error TS2459: Type '{ x: number; y: string; z: boolean; }' has no property 'x2' and no string index signature. -tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(5,35): error TS2459: Type '{ x: number; y: string; z: boolean; }' has no property 'x3' and no string index signature. +tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(5,27): error TS2459: Type 'ObjType1' has no property 'x1' and no string index signature. +tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(5,31): error TS2459: Type 'ObjType1' has no property 'x2' and no string index signature. +tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(5,35): error TS2459: Type 'ObjType1' has no property 'x3' and no string index signature. tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7,29): error TS2339: Property 'x1' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7,40): error TS2339: Property 'x2' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7,51): error TS2339: Property 'x3' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7,62): error TS2339: Property 'y' does not exist on type 'C1'. tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(7,72): error TS2339: Property 'z' does not exist on type 'C1'. -tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(11,19): error TS2345: Argument of type '[{ x1: number; x2: string; x3: boolean; }, string, boolean]' is not assignable to parameter of type '[{ x: number; y: string; z: boolean; }, number, string]'. - Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type '{ x: number; y: string; z: boolean; }'. - Object literal may only specify known properties, and 'x1' does not exist in type '{ x: number; y: string; z: boolean; }'. +tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(11,19): error TS2345: Argument of type '[{ x1: number; x2: string; x3: boolean; }, string, boolean]' is not assignable to parameter of type '[ObjType1, number, string]'. + Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type 'ObjType1'. + Object literal may only specify known properties, and 'x1' does not exist in type 'ObjType1'. ==== tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts (10 errors) ==== @@ -21,11 +21,11 @@ tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS1187: A parameter property may not be declared using a binding pattern. ~~ -!!! error TS2459: Type '{ x: number; y: string; z: boolean; }' has no property 'x1' and no string index signature. +!!! error TS2459: Type 'ObjType1' has no property 'x1' and no string index signature. ~~ -!!! error TS2459: Type '{ x: number; y: string; z: boolean; }' has no property 'x2' and no string index signature. +!!! error TS2459: Type 'ObjType1' has no property 'x2' and no string index signature. ~~ -!!! error TS2459: Type '{ x: number; y: string; z: boolean; }' has no property 'x3' and no string index signature. +!!! error TS2459: Type 'ObjType1' has no property 'x3' and no string index signature. var foo: any = x1 || x2 || x3 || y || z; var bar: any = this.x1 || this.x2 || this.x3 || this.y || this.z; ~~ @@ -43,7 +43,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterProperties5.ts(1 var a = new C1([{ x1: 10, x2: "", x3: true }, "", false]); ~~~~~~ -!!! error TS2345: Argument of type '[{ x1: number; x2: string; x3: boolean; }, string, boolean]' is not assignable to parameter of type '[{ x: number; y: string; z: boolean; }, number, string]'. -!!! error TS2345: Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type '{ x: number; y: string; z: boolean; }'. -!!! error TS2345: Object literal may only specify known properties, and 'x1' does not exist in type '{ x: number; y: string; z: boolean; }'. +!!! error TS2345: Argument of type '[{ x1: number; x2: string; x3: boolean; }, string, boolean]' is not assignable to parameter of type '[ObjType1, number, string]'. +!!! error TS2345: Type '{ x1: number; x2: string; x3: boolean; }' is not assignable to type 'ObjType1'. +!!! error TS2345: Object literal may only specify known properties, and 'x1' does not exist in type 'ObjType1'. var [a_x1, a_x2, a_x3, a_y, a_z] = [a.x1, a.x2, a.x3, a.y, a.z]; \ No newline at end of file diff --git a/tests/baselines/reference/implicitIndexSignatures.types b/tests/baselines/reference/implicitIndexSignatures.types index ac8da0c7b6c2a..0b68f136e2e5f 100644 --- a/tests/baselines/reference/implicitIndexSignatures.types +++ b/tests/baselines/reference/implicitIndexSignatures.types @@ -1,6 +1,6 @@ === tests/cases/compiler/implicitIndexSignatures.ts === type StringMap = { [x: string]: string }; ->StringMap : { [x: string]: string; } +>StringMap : StringMap >x : string const empty1 = {}; @@ -24,12 +24,12 @@ let names2: { a: string, b: string }; >b : string let map: StringMap; ->map : { [x: string]: string; } ->StringMap : { [x: string]: string; } +>map : StringMap +>StringMap : StringMap map = { x: "xxx", y: "yyy" }; >map = { x: "xxx", y: "yyy" } : { x: string; y: string; } ->map : { [x: string]: string; } +>map : StringMap >{ x: "xxx", y: "yyy" } : { x: string; y: string; } >x : string >"xxx" : "xxx" @@ -38,22 +38,22 @@ map = { x: "xxx", y: "yyy" }; map = empty1; >map = empty1 : {} ->map : { [x: string]: string; } +>map : StringMap >empty1 : {} map = empty2; >map = empty2 : {} ->map : { [x: string]: string; } +>map : StringMap >empty2 : {} map = names1; >map = names1 : { a: string; b: string; } ->map : { [x: string]: string; } +>map : StringMap >names1 : { a: string; b: string; } map = names2; >map = names2 : { a: string; b: string; } ->map : { [x: string]: string; } +>map : StringMap >names2 : { a: string; b: string; } declare function getStringIndexValue(map: { [x: string]: T }): T; diff --git a/tests/baselines/reference/intersectionTypeNormalization.types b/tests/baselines/reference/intersectionTypeNormalization.types index 10ef50ae1b45a..99d0ecb1f3ab0 100644 --- a/tests/baselines/reference/intersectionTypeNormalization.types +++ b/tests/baselines/reference/intersectionTypeNormalization.types @@ -189,7 +189,7 @@ var z: Z4; // Repro from #9919 type ToString = { ->ToString : { toString(): string; } +>ToString : ToString toString(): string; >toString : () => string @@ -207,18 +207,18 @@ type BoxedValue = { kind: 'int', num: number } type IntersectionFail = BoxedValue & ToString >IntersectionFail : IntersectionFail >BoxedValue : BoxedValue ->ToString : { toString(): string; } +>ToString : ToString type IntersectionInline = { kind: 'int', num: number } & ToString >IntersectionInline : IntersectionInline >kind : "int" >num : number ->ToString : { toString(): string; } +>ToString : ToString | { kind: 'string', str: string } & ToString >kind : "string" >str : string ->ToString : { toString(): string; } +>ToString : ToString function getValueAsString(value: IntersectionFail): string { >getValueAsString : (value: IntersectionFail) => string @@ -236,11 +236,11 @@ function getValueAsString(value: IntersectionFail): string { >'' + value.num : string >'' : "" >value.num : number ->value : { kind: "int"; num: number; } & { toString(): string; } +>value : { kind: "int"; num: number; } & ToString >num : number } return value.str; >value.str : string ->value : { kind: "string"; str: string; } & { toString(): string; } +>value : { kind: "string"; str: string; } & ToString >str : string } diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index a5395db8292ae..c252ea4e2bd97 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -42,7 +42,7 @@ class Options { } type Dictionary = { [x: string]: T }; ->Dictionary : { [x: string]: T; } +>Dictionary : Dictionary >T : T >x : string >T : T @@ -88,7 +88,7 @@ type K11 = keyof Shape[]; // number | "length" | "toString" | ... type K12 = keyof Dictionary; // string | number >K12 : string | number ->Dictionary : { [x: string]: T; } +>Dictionary : Dictionary >Shape : Shape type K13 = keyof {}; // never @@ -128,7 +128,7 @@ type K20 = KeyOf; // "name" | "width" | "height" | "visible" type K21 = KeyOf>; // string | number >K21 : string | number >KeyOf : keyof T ->Dictionary : { [x: string]: T; } +>Dictionary : Dictionary >Shape : Shape type NAME = "name"; @@ -201,17 +201,17 @@ type Q41 = (Shape & Options)["visible"]; // true & "yes" | true & "no" | false type Q50 = Dictionary["howdy"]; // Shape >Q50 : Shape ->Dictionary : { [x: string]: T; } +>Dictionary : Dictionary >Shape : Shape type Q51 = Dictionary[123]; // Shape >Q51 : Shape ->Dictionary : { [x: string]: T; } +>Dictionary : Dictionary >Shape : Shape type Q52 = Dictionary[E.B]; // Shape >Q52 : Shape ->Dictionary : { [x: string]: T; } +>Dictionary : Dictionary >Shape : Shape >E : any >B : E.B diff --git a/tests/baselines/reference/noErrorTruncation.errors.txt b/tests/baselines/reference/noErrorTruncation.errors.txt index af3acb54bba89..d5244df81a893 100644 --- a/tests/baselines/reference/noErrorTruncation.errors.txt +++ b/tests/baselines/reference/noErrorTruncation.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/noErrorTruncation.ts(10,7): error TS2322: Type '42' is not assignable to type '{ someLongOptionA: string; } | { someLongOptionB: string; } | { someLongOptionC: string; } | { someLongOptionD: string; } | { someLongOptionE: string; } | { someLongOptionF: string; }'. +tests/cases/compiler/noErrorTruncation.ts(10,7): error TS2322: Type '42' is not assignable to type 'SomeLongOptionA | SomeLongOptionB | SomeLongOptionC | SomeLongOptionD | SomeLongOptionE | SomeLongOptionF'. ==== tests/cases/compiler/noErrorTruncation.ts (1 errors) ==== @@ -13,7 +13,7 @@ tests/cases/compiler/noErrorTruncation.ts(10,7): error TS2322: Type '42' is not const x: SomeLongOptionA ~ -!!! error TS2322: Type '42' is not assignable to type '{ someLongOptionA: string; } | { someLongOptionB: string; } | { someLongOptionC: string; } | { someLongOptionD: string; } | { someLongOptionE: string; } | { someLongOptionF: string; }'. +!!! error TS2322: Type '42' is not assignable to type 'SomeLongOptionA | SomeLongOptionB | SomeLongOptionC | SomeLongOptionD | SomeLongOptionE | SomeLongOptionF'. | SomeLongOptionB | SomeLongOptionC | SomeLongOptionD diff --git a/tests/baselines/reference/nounusedTypeParameterConstraint.types b/tests/baselines/reference/nounusedTypeParameterConstraint.types index 8f2de483d2693..0ded5540092d9 100644 --- a/tests/baselines/reference/nounusedTypeParameterConstraint.types +++ b/tests/baselines/reference/nounusedTypeParameterConstraint.types @@ -8,7 +8,7 @@ import { IEventSourcedEntity } from "./bar"; >IEventSourcedEntity : any export type DomainEntityConstructor = { new(): TEntity; }; ->DomainEntityConstructor : new () => TEntity +>DomainEntityConstructor : DomainEntityConstructor >TEntity : TEntity >IEventSourcedEntity : IEventSourcedEntity >TEntity : TEntity diff --git a/tests/baselines/reference/typeAliasDeclarationEmit2.types b/tests/baselines/reference/typeAliasDeclarationEmit2.types index bc8bd30935e8b..2cbee5aae4cb7 100644 --- a/tests/baselines/reference/typeAliasDeclarationEmit2.types +++ b/tests/baselines/reference/typeAliasDeclarationEmit2.types @@ -1,7 +1,7 @@ === tests/cases/compiler/typeAliasDeclarationEmit2.ts === export type A = { value: a }; ->A : { value: a; } +>A : A >a : a >value : a >a : a diff --git a/tests/baselines/reference/typeAliases.types b/tests/baselines/reference/typeAliases.types index 1d798bc14a64a..6c8e897480c8d 100644 --- a/tests/baselines/reference/typeAliases.types +++ b/tests/baselines/reference/typeAliases.types @@ -104,7 +104,7 @@ var x9: T9; >T9 : T9 type T10 = { x: number }; ->T10 : { x: number; } +>T10 : T10 >x : number var x10: { x: number }; @@ -113,17 +113,17 @@ var x10: { x: number }; var x10: T10; >x10 : { x: number; } ->T10 : { x: number; } +>T10 : T10 type T11 = { new(): boolean }; ->T11 : new () => boolean +>T11 : T11 var x11: { new(): boolean }; >x11 : new () => boolean var x11: T11; >x11 : new () => boolean ->T11 : new () => boolean +>T11 : T11 interface I13 { x: string }; >I13 : I13 diff --git a/tests/baselines/reference/typeArgumentInferenceWithRecursivelyReferencedTypeAliasToTypeLiteral02.types b/tests/baselines/reference/typeArgumentInferenceWithRecursivelyReferencedTypeAliasToTypeLiteral02.types index 6e3997c2d1fc9..a75a997eb789b 100644 --- a/tests/baselines/reference/typeArgumentInferenceWithRecursivelyReferencedTypeAliasToTypeLiteral02.types +++ b/tests/baselines/reference/typeArgumentInferenceWithRecursivelyReferencedTypeAliasToTypeLiteral02.types @@ -11,7 +11,7 @@ type TreeNode = { } type TreeNodeMiddleman = { ->TreeNodeMiddleman : { name: string; parent: TreeNode; } +>TreeNodeMiddleman : TreeNodeMiddleman name: string; >name : string @@ -22,17 +22,17 @@ type TreeNodeMiddleman = { } var nodes: TreeNodeMiddleman[]; ->nodes : { name: string; parent: TreeNode; }[] ->TreeNodeMiddleman : { name: string; parent: TreeNode; } +>nodes : TreeNodeMiddleman[] +>TreeNodeMiddleman : TreeNodeMiddleman nodes.map(n => n.name); >nodes.map(n => n.name) : string[] ->nodes.map : { (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U, U, U, U]; (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U, U, U]; (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U, U]; (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U]; (callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): U[]; } ->nodes : { name: string; parent: TreeNode; }[] ->map : { (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U, U, U, U]; (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U, U, U]; (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U, U]; (this: [{ name: string; parent: TreeNode; }, { name: string; parent: TreeNode; }], callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): [U, U]; (callbackfn: (value: { name: string; parent: TreeNode; }, index: number, array: { name: string; parent: TreeNode; }[]) => U, thisArg?: any): U[]; } ->n => n.name : (n: { name: string; parent: TreeNode; }) => string ->n : { name: string; parent: TreeNode; } +>nodes.map : { (this: [TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U, U, U, U]; (this: [TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U, U, U]; (this: [TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U, U]; (this: [TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U]; (callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): U[]; } +>nodes : TreeNodeMiddleman[] +>map : { (this: [TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U, U, U, U]; (this: [TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U, U, U]; (this: [TreeNodeMiddleman, TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U, U]; (this: [TreeNodeMiddleman, TreeNodeMiddleman], callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): [U, U]; (callbackfn: (value: TreeNodeMiddleman, index: number, array: TreeNodeMiddleman[]) => U, thisArg?: any): U[]; } +>n => n.name : (n: TreeNodeMiddleman) => string +>n : TreeNodeMiddleman >n.name : string ->n : { name: string; parent: TreeNode; } +>n : TreeNodeMiddleman >name : string diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types index 478363669b461..f793ca34548c6 100644 --- a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types @@ -1,18 +1,18 @@ === tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts === type Tag = {__tag: any}; ->Tag : { __tag: any; } +>Tag : Tag >__tag : any declare function isNonBlank(value: string) : value is (string & Tag); ->isNonBlank : (value: string) => value is string & { __tag: any; } +>isNonBlank : (value: string) => value is string & Tag >value : string >value : any ->Tag : { __tag: any; } +>Tag : Tag declare function doThis(value: string & Tag): void; ->doThis : (value: string & { __tag: any; }) => void ->value : string & { __tag: any; } ->Tag : { __tag: any; } +>doThis : (value: string & Tag) => void +>value : string & Tag +>Tag : Tag declare function doThat(value: string) : void; >doThat : (value: string) => void @@ -23,13 +23,13 @@ let value: string; if (isNonBlank(value)) { >isNonBlank(value) : boolean ->isNonBlank : (value: string) => value is string & { __tag: any; } +>isNonBlank : (value: string) => value is string & Tag >value : string doThis(value); >doThis(value) : void ->doThis : (value: string & { __tag: any; }) => void ->value : string & { __tag: any; } +>doThis : (value: string & Tag) => void +>value : string & Tag } else { doThat(value); From aca1ab3bfe485ad27d7e21d240f29bcf31fd380a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 10 Nov 2016 10:41:17 -0800 Subject: [PATCH 19/28] Check mapped type constraint is assignable to string | number --- src/compiler/checker.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8f7d8aa0d6126..8aa9a03da0446 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15316,11 +15316,12 @@ namespace ts { } function checkMappedType(node: MappedTypeNode) { - getTypeFromMappedTypeNode(node); - // const type = getTypeFromMappedTypeNode(node); - // const constraintType = getConstraintTypeFromMappedType(type); - // const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(constraintType) : constraintType; - // checkTypeAssignableTo(keyType, stringOrNumberType, node.typeParameter.constraint); + checkSourceElement(node.typeParameter); + checkSourceElement(node.type); + const type = getTypeFromMappedTypeNode(node); + const constraintType = getConstraintTypeFromMappedType(type); + const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentTypeOfTypeParameter(constraintType) : constraintType; + checkTypeAssignableTo(keyType, stringOrNumberType, node.typeParameter.constraint); } function isPrivateWithinAmbient(node: Node): boolean { From cd185f2cf6cc32969e6d033e40c4738efb5ba79d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 10 Nov 2016 15:20:29 -0800 Subject: [PATCH 20/28] Add declaration emit support --- src/compiler/declarationEmitter.ts | 32 ++++++++++++++++++++++++++++-- src/compiler/emitter.ts | 29 +++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 0bba375a2cb2e..7d2f61b6d6f00 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -416,7 +416,9 @@ namespace ts { case SyntaxKind.TypeOperator: return emitTypeOperator(type); case SyntaxKind.IndexedAccessType: - return emitPropertyAccessType(type); + return emitIndexedAccessType(type); + case SyntaxKind.MappedType: + return emitMappedType(type); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: return emitSignatureDeclarationWithJsDocComments(type); @@ -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) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index fa0d44a695f54..a18146bdaa1fa 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -606,7 +606,9 @@ const _super = (function (geti, seti) { case SyntaxKind.TypeOperator: return emitTypeOperator(node); case SyntaxKind.IndexedAccessType: - return emitPropertyAccessType(node); + return emitIndexedAccessType(node); + case SyntaxKind.MappedType: + return emitMappedType(node); case SyntaxKind.LiteralType: return emitLiteralType(node); @@ -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); } From 1c7ec6b8cef4290a7b7ea5d647907a067ae98ec5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 11 Nov 2016 07:38:44 -0800 Subject: [PATCH 21/28] Add missing node visits in forEachChild --- src/compiler/parser.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7f112ae06a041..02b121c31b6dc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -142,7 +142,9 @@ namespace ts { return visitNode(cbNode, (node).objectType) || visitNode(cbNode, (node).indexType); case SyntaxKind.MappedType: - return visitNode(cbNode, (node).typeParameter) || + return visitNode(cbNode, (node).readonlyToken) || + visitNode(cbNode, (node).typeParameter) || + visitNode(cbNode, (node).questionToken) || visitNode(cbNode, (node).type); case SyntaxKind.LiteralType: return visitNode(cbNode, (node).literal); From 364142c06235b58cf14df7e2c1c927a76adeb75b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 11 Nov 2016 07:39:51 -0800 Subject: [PATCH 22/28] Improve type inference for types with same generic type alias --- src/compiler/checker.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 90d6078c8c94d..01ea222721737 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8318,6 +8318,16 @@ namespace ts { if (!couldContainTypeParameters(target)) { return; } + if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) { + // Source and target are types originating in the same generic type alias declaration. + // Simply infer from source type arguments to target type arguments. + const sourceTypes = source.aliasTypeArguments; + const targetTypes = target.aliasTypeArguments; + for (let i = 0; i < sourceTypes.length; i++) { + inferFromTypes(sourceTypes[i], targetTypes[i]); + } + return; + } if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && !(source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) || source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { // Source and target are both unions or both intersections. If source and target From e9b6ddc9ae02fc524a34940db4082e98b08508f2 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 11 Nov 2016 07:40:05 -0800 Subject: [PATCH 23/28] Add tests --- tests/baselines/reference/mappedTypes1.js | 146 ++++++++ .../baselines/reference/mappedTypes1.symbols | 138 +++++++ tests/baselines/reference/mappedTypes1.types | 143 +++++++ tests/baselines/reference/mappedTypes2.js | 183 +++++++++ .../baselines/reference/mappedTypes2.symbols | 331 ++++++++++++++++ tests/baselines/reference/mappedTypes2.types | 353 ++++++++++++++++++ tests/baselines/reference/mappedTypes3.js | 84 +++++ .../baselines/reference/mappedTypes3.symbols | 135 +++++++ tests/baselines/reference/mappedTypes3.types | 138 +++++++ .../conformance/types/mapped/mappedTypes1.ts | 35 ++ .../conformance/types/mapped/mappedTypes2.ts | 96 +++++ .../conformance/types/mapped/mappedTypes3.ts | 40 ++ 12 files changed, 1822 insertions(+) create mode 100644 tests/baselines/reference/mappedTypes1.js create mode 100644 tests/baselines/reference/mappedTypes1.symbols create mode 100644 tests/baselines/reference/mappedTypes1.types create mode 100644 tests/baselines/reference/mappedTypes2.js create mode 100644 tests/baselines/reference/mappedTypes2.symbols create mode 100644 tests/baselines/reference/mappedTypes2.types create mode 100644 tests/baselines/reference/mappedTypes3.js create mode 100644 tests/baselines/reference/mappedTypes3.symbols create mode 100644 tests/baselines/reference/mappedTypes3.types create mode 100644 tests/cases/conformance/types/mapped/mappedTypes1.ts create mode 100644 tests/cases/conformance/types/mapped/mappedTypes2.ts create mode 100644 tests/cases/conformance/types/mapped/mappedTypes3.ts diff --git a/tests/baselines/reference/mappedTypes1.js b/tests/baselines/reference/mappedTypes1.js new file mode 100644 index 0000000000000..df03a0229662c --- /dev/null +++ b/tests/baselines/reference/mappedTypes1.js @@ -0,0 +1,146 @@ +//// [mappedTypes1.ts] + +type Item = { a: string, b: number, c: boolean }; + +type T00 = { [P in "x" | "y"]: number }; +type T01 = { [P in "x" | "y"]: P }; +type T02 = { [P in "a" | "b"]: Item[P]; } +type T03 = { [P in keyof Item]: Date }; + +type T10 = { [P in keyof Item]: Item[P] }; +type T11 = { [P in keyof Item]?: Item[P] }; +type T12 = { readonly [P in keyof Item]: Item[P] }; +type T13 = { readonly [P in keyof Item]?: Item[P] }; + +type T20 = { [P in keyof Item]: Item[P] | null }; +type T21 = { [P in keyof Item]: Array }; + +type T30 = { [P in keyof any]: void }; +type T31 = { [P in keyof string]: void }; +type T32 = { [P in keyof number]: void }; +type T33 = { [P in keyof boolean]: void }; +type T34 = { [P in keyof undefined]: void }; +type T35 = { [P in keyof null]: void }; +type T36 = { [P in keyof void]: void }; +type T37 = { [P in keyof symbol]: void }; +type T38 = { [P in keyof never]: void }; + +declare function f1(): { [P in keyof T1]: void }; +declare function f2(): { [P in keyof T1]: void }; +declare function f3(): { [P in keyof T1]: void }; + +let x1 = f1(); +let x2 = f2(); +let x3 = f3(); + +//// [mappedTypes1.js] +var x1 = f1(); +var x2 = f2(); +var x3 = f3(); + + +//// [mappedTypes1.d.ts] +declare type Item = { + a: string; + b: number; + c: boolean; +}; +declare type T00 = { + [P in "x" | "y"]: number; +}; +declare type T01 = { + [P in "x" | "y"]: P; +}; +declare type T02 = { + [P in "a" | "b"]: Item[P]; +}; +declare type T03 = { + [P in keyof Item]: Date; +}; +declare type T10 = { + [P in keyof Item]: Item[P]; +}; +declare type T11 = { + [P in keyof Item]?: Item[P]; +}; +declare type T12 = { + readonly [P in keyof Item]: Item[P]; +}; +declare type T13 = { + readonly [P in keyof Item]?: Item[P]; +}; +declare type T20 = { + [P in keyof Item]: Item[P] | null; +}; +declare type T21 = { + [P in keyof Item]: Array; +}; +declare type T30 = { + [P in keyof any]: void; +}; +declare type T31 = { + [P in keyof string]: void; +}; +declare type T32 = { + [P in keyof number]: void; +}; +declare type T33 = { + [P in keyof boolean]: void; +}; +declare type T34 = { + [P in keyof undefined]: void; +}; +declare type T35 = { + [P in keyof null]: void; +}; +declare type T36 = { + [P in keyof void]: void; +}; +declare type T37 = { + [P in keyof symbol]: void; +}; +declare type T38 = { + [P in keyof never]: void; +}; +declare function f1(): { + [P in keyof T1]: void; +}; +declare function f2(): { + [P in keyof T1]: void; +}; +declare function f3(): { + [P in keyof T1]: void; +}; +declare let x1: {}; +declare let x2: { + [x: number]: void; + toString: void; + charAt: void; + charCodeAt: void; + concat: void; + indexOf: void; + lastIndexOf: void; + localeCompare: void; + match: void; + replace: void; + search: void; + slice: void; + split: void; + substring: void; + toLowerCase: void; + toLocaleLowerCase: void; + toUpperCase: void; + toLocaleUpperCase: void; + trim: void; + length: void; + substr: void; + valueOf: void; +}; +declare let x3: { + toString: void; + valueOf: void; + toFixed: void; + toExponential: void; + toPrecision: void; + toLocaleString: void; +}; diff --git a/tests/baselines/reference/mappedTypes1.symbols b/tests/baselines/reference/mappedTypes1.symbols new file mode 100644 index 0000000000000..65732a4be11d9 --- /dev/null +++ b/tests/baselines/reference/mappedTypes1.symbols @@ -0,0 +1,138 @@ +=== tests/cases/conformance/types/mapped/mappedTypes1.ts === + +type Item = { a: string, b: number, c: boolean }; +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>a : Symbol(a, Decl(mappedTypes1.ts, 1, 13)) +>b : Symbol(b, Decl(mappedTypes1.ts, 1, 24)) +>c : Symbol(c, Decl(mappedTypes1.ts, 1, 35)) + +type T00 = { [P in "x" | "y"]: number }; +>T00 : Symbol(T00, Decl(mappedTypes1.ts, 1, 49)) +>P : Symbol(P, Decl(mappedTypes1.ts, 3, 14)) + +type T01 = { [P in "x" | "y"]: P }; +>T01 : Symbol(T01, Decl(mappedTypes1.ts, 3, 40)) +>P : Symbol(P, Decl(mappedTypes1.ts, 4, 14)) +>P : Symbol(P, Decl(mappedTypes1.ts, 4, 14)) + +type T02 = { [P in "a" | "b"]: Item[P]; } +>T02 : Symbol(T02, Decl(mappedTypes1.ts, 4, 35)) +>P : Symbol(P, Decl(mappedTypes1.ts, 5, 14)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes1.ts, 5, 14)) + +type T03 = { [P in keyof Item]: Date }; +>T03 : Symbol(T03, Decl(mappedTypes1.ts, 5, 41)) +>P : Symbol(P, Decl(mappedTypes1.ts, 6, 14)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +type T10 = { [P in keyof Item]: Item[P] }; +>T10 : Symbol(T10, Decl(mappedTypes1.ts, 6, 39)) +>P : Symbol(P, Decl(mappedTypes1.ts, 8, 14)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes1.ts, 8, 14)) + +type T11 = { [P in keyof Item]?: Item[P] }; +>T11 : Symbol(T11, Decl(mappedTypes1.ts, 8, 42)) +>P : Symbol(P, Decl(mappedTypes1.ts, 9, 14)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes1.ts, 9, 14)) + +type T12 = { readonly [P in keyof Item]: Item[P] }; +>T12 : Symbol(T12, Decl(mappedTypes1.ts, 9, 43)) +>P : Symbol(P, Decl(mappedTypes1.ts, 10, 23)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes1.ts, 10, 23)) + +type T13 = { readonly [P in keyof Item]?: Item[P] }; +>T13 : Symbol(T13, Decl(mappedTypes1.ts, 10, 51)) +>P : Symbol(P, Decl(mappedTypes1.ts, 11, 23)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes1.ts, 11, 23)) + +type T20 = { [P in keyof Item]: Item[P] | null }; +>T20 : Symbol(T20, Decl(mappedTypes1.ts, 11, 52)) +>P : Symbol(P, Decl(mappedTypes1.ts, 13, 14)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes1.ts, 13, 14)) + +type T21 = { [P in keyof Item]: Array }; +>T21 : Symbol(T21, Decl(mappedTypes1.ts, 13, 49)) +>P : Symbol(P, Decl(mappedTypes1.ts, 14, 14)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Item : Symbol(Item, Decl(mappedTypes1.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes1.ts, 14, 14)) + +type T30 = { [P in keyof any]: void }; +>T30 : Symbol(T30, Decl(mappedTypes1.ts, 14, 49)) +>P : Symbol(P, Decl(mappedTypes1.ts, 16, 14)) + +type T31 = { [P in keyof string]: void }; +>T31 : Symbol(T31, Decl(mappedTypes1.ts, 16, 38)) +>P : Symbol(P, Decl(mappedTypes1.ts, 17, 14)) + +type T32 = { [P in keyof number]: void }; +>T32 : Symbol(T32, Decl(mappedTypes1.ts, 17, 41)) +>P : Symbol(P, Decl(mappedTypes1.ts, 18, 14)) + +type T33 = { [P in keyof boolean]: void }; +>T33 : Symbol(T33, Decl(mappedTypes1.ts, 18, 41)) +>P : Symbol(P, Decl(mappedTypes1.ts, 19, 14)) + +type T34 = { [P in keyof undefined]: void }; +>T34 : Symbol(T34, Decl(mappedTypes1.ts, 19, 42)) +>P : Symbol(P, Decl(mappedTypes1.ts, 20, 14)) + +type T35 = { [P in keyof null]: void }; +>T35 : Symbol(T35, Decl(mappedTypes1.ts, 20, 44)) +>P : Symbol(P, Decl(mappedTypes1.ts, 21, 14)) + +type T36 = { [P in keyof void]: void }; +>T36 : Symbol(T36, Decl(mappedTypes1.ts, 21, 39)) +>P : Symbol(P, Decl(mappedTypes1.ts, 22, 14)) + +type T37 = { [P in keyof symbol]: void }; +>T37 : Symbol(T37, Decl(mappedTypes1.ts, 22, 39)) +>P : Symbol(P, Decl(mappedTypes1.ts, 23, 14)) + +type T38 = { [P in keyof never]: void }; +>T38 : Symbol(T38, Decl(mappedTypes1.ts, 23, 41)) +>P : Symbol(P, Decl(mappedTypes1.ts, 24, 14)) + +declare function f1(): { [P in keyof T1]: void }; +>f1 : Symbol(f1, Decl(mappedTypes1.ts, 24, 40)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 26, 20)) +>P : Symbol(P, Decl(mappedTypes1.ts, 26, 30)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 26, 20)) + +declare function f2(): { [P in keyof T1]: void }; +>f2 : Symbol(f2, Decl(mappedTypes1.ts, 26, 53)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 27, 20)) +>P : Symbol(P, Decl(mappedTypes1.ts, 27, 45)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 27, 20)) + +declare function f3(): { [P in keyof T1]: void }; +>f3 : Symbol(f3, Decl(mappedTypes1.ts, 27, 68)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 28, 20)) +>P : Symbol(P, Decl(mappedTypes1.ts, 28, 45)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 28, 20)) + +let x1 = f1(); +>x1 : Symbol(x1, Decl(mappedTypes1.ts, 30, 3)) +>f1 : Symbol(f1, Decl(mappedTypes1.ts, 24, 40)) + +let x2 = f2(); +>x2 : Symbol(x2, Decl(mappedTypes1.ts, 31, 3)) +>f2 : Symbol(f2, Decl(mappedTypes1.ts, 26, 53)) + +let x3 = f3(); +>x3 : Symbol(x3, Decl(mappedTypes1.ts, 32, 3)) +>f3 : Symbol(f3, Decl(mappedTypes1.ts, 27, 68)) + diff --git a/tests/baselines/reference/mappedTypes1.types b/tests/baselines/reference/mappedTypes1.types new file mode 100644 index 0000000000000..222291b008b59 --- /dev/null +++ b/tests/baselines/reference/mappedTypes1.types @@ -0,0 +1,143 @@ +=== tests/cases/conformance/types/mapped/mappedTypes1.ts === + +type Item = { a: string, b: number, c: boolean }; +>Item : Item +>a : string +>b : number +>c : boolean + +type T00 = { [P in "x" | "y"]: number }; +>T00 : T00 +>P : P + +type T01 = { [P in "x" | "y"]: P }; +>T01 : T01 +>P : P +>P : P + +type T02 = { [P in "a" | "b"]: Item[P]; } +>T02 : T02 +>P : P +>Item : Item +>P : P + +type T03 = { [P in keyof Item]: Date }; +>T03 : T03 +>P : P +>Item : Item +>Date : Date + +type T10 = { [P in keyof Item]: Item[P] }; +>T10 : T10 +>P : P +>Item : Item +>Item : Item +>P : P + +type T11 = { [P in keyof Item]?: Item[P] }; +>T11 : T11 +>P : P +>Item : Item +>Item : Item +>P : P + +type T12 = { readonly [P in keyof Item]: Item[P] }; +>T12 : T12 +>P : P +>Item : Item +>Item : Item +>P : P + +type T13 = { readonly [P in keyof Item]?: Item[P] }; +>T13 : T13 +>P : P +>Item : Item +>Item : Item +>P : P + +type T20 = { [P in keyof Item]: Item[P] | null }; +>T20 : T20 +>P : P +>Item : Item +>Item : Item +>P : P +>null : null + +type T21 = { [P in keyof Item]: Array }; +>T21 : T21 +>P : P +>Item : Item +>Array : T[] +>Item : Item +>P : P + +type T30 = { [P in keyof any]: void }; +>T30 : T30 +>P : P + +type T31 = { [P in keyof string]: void }; +>T31 : T31 +>P : P + +type T32 = { [P in keyof number]: void }; +>T32 : T32 +>P : P + +type T33 = { [P in keyof boolean]: void }; +>T33 : T33 +>P : P + +type T34 = { [P in keyof undefined]: void }; +>T34 : T34 +>P : P + +type T35 = { [P in keyof null]: void }; +>T35 : T35 +>P : P +>null : null + +type T36 = { [P in keyof void]: void }; +>T36 : T36 +>P : P + +type T37 = { [P in keyof symbol]: void }; +>T37 : T37 +>P : P + +type T38 = { [P in keyof never]: void }; +>T38 : T38 +>P : P + +declare function f1(): { [P in keyof T1]: void }; +>f1 : () => { [P in keyof T1]: void; } +>T1 : T1 +>P : P +>T1 : T1 + +declare function f2(): { [P in keyof T1]: void }; +>f2 : () => { [P in keyof T1]: void; } +>T1 : T1 +>P : P +>T1 : T1 + +declare function f3(): { [P in keyof T1]: void }; +>f3 : () => { [P in keyof T1]: void; } +>T1 : T1 +>P : P +>T1 : T1 + +let x1 = f1(); +>x1 : {} +>f1() : {} +>f1 : () => { [P in keyof T1]: void; } + +let x2 = f2(); +>x2 : { [x: number]: void; toString: void; charAt: void; charCodeAt: void; concat: void; indexOf: void; lastIndexOf: void; localeCompare: void; match: void; replace: void; search: void; slice: void; split: void; substring: void; toLowerCase: void; toLocaleLowerCase: void; toUpperCase: void; toLocaleUpperCase: void; trim: void; length: void; substr: void; valueOf: void; } +>f2() : { [x: number]: void; toString: void; charAt: void; charCodeAt: void; concat: void; indexOf: void; lastIndexOf: void; localeCompare: void; match: void; replace: void; search: void; slice: void; split: void; substring: void; toLowerCase: void; toLocaleLowerCase: void; toUpperCase: void; toLocaleUpperCase: void; trim: void; length: void; substr: void; valueOf: void; } +>f2 : () => { [P in keyof T1]: void; } + +let x3 = f3(); +>x3 : { toString: void; valueOf: void; toFixed: void; toExponential: void; toPrecision: void; toLocaleString: void; } +>f3() : { toString: void; valueOf: void; toFixed: void; toExponential: void; toPrecision: void; toLocaleString: void; } +>f3 : () => { [P in keyof T1]: void; } + diff --git a/tests/baselines/reference/mappedTypes2.js b/tests/baselines/reference/mappedTypes2.js new file mode 100644 index 0000000000000..8796244c390f7 --- /dev/null +++ b/tests/baselines/reference/mappedTypes2.js @@ -0,0 +1,183 @@ +//// [mappedTypes2.ts] + +type Partial = { + [P in keyof T]?: T[P]; +}; + +type Readonly = { + readonly [P in keyof T]: T[P]; +}; + +type Pick = { + [P in K]: T[P]; +} + +type Record = { + [_ in K]: T; +} + +type Proxy = { + get(): T; + set(value: T): void; +} + +type Proxify = { + [P in keyof T]: Proxy; +} + +type DeepReadonly = { + readonly [P in keyof T]: DeepReadonly; +}; + +declare function assign(obj: T, props: Partial): void; +declare function freeze(obj: T): Readonly; +declare function pick(obj: T, ...keys: K[]): Pick; +declare function mapObject(obj: Record, f: (x: T) => U): Record; +declare function proxify(obj: T): Proxify; + +interface Shape { + name: string; + width: number; + height: number; + visible: boolean; +} + +interface PartialShape { + name?: string; + width?: number; + height?: number; + visible?: boolean; +} + +interface ReadonlyShape { + readonly name: string; + readonly width: number; + readonly height: number; + readonly visible: boolean; +} + +function f0(s1: Shape, s2: Shape) { + assign(s1, { name: "circle" }); + assign(s2, { width: 10, height: 20 }); +} + +function f1(shape: Shape) { + var frozen: ReadonlyShape; + var frozen: Readonly; + var frozen = freeze(shape); +} + +function f2(shape: Shape) { + var partial: PartialShape; + var partial: Partial; + var partial: Partial = {}; +} + +function f3(shape: Shape) { + const x = pick(shape, "name", "visible"); // { name: string, visible: boolean } +} + +function f4() { + const rec = { foo: "hello", bar: "world", baz: "bye" }; + const lengths = mapObject(rec, s => s.length); // { foo: number, bar: number, baz: number } +} + +function f5(shape: Shape) { + const p = proxify(shape); + let name = p.name.get(); + p.visible.set(false); +} + +function f6(shape: DeepReadonly) { + let name = shape.name; // DeepReadonly + let length = name.length; // DeepReadonly + let toString = length.toString; // DeepReadonly<(radix?: number) => string> +} + +//// [mappedTypes2.js] +function f0(s1, s2) { + assign(s1, { name: "circle" }); + assign(s2, { width: 10, height: 20 }); +} +function f1(shape) { + var frozen; + var frozen; + var frozen = freeze(shape); +} +function f2(shape) { + var partial; + var partial; + var partial = {}; +} +function f3(shape) { + var x = pick(shape, "name", "visible"); // { name: string, visible: boolean } +} +function f4() { + var rec = { foo: "hello", bar: "world", baz: "bye" }; + var lengths = mapObject(rec, function (s) { return s.length; }); // { foo: number, bar: number, baz: number } +} +function f5(shape) { + var p = proxify(shape); + var name = p.name.get(); + p.visible.set(false); +} +function f6(shape) { + var name = shape.name; // DeepReadonly + var length = name.length; // DeepReadonly + var toString = length.toString; // DeepReadonly<(radix?: number) => string> +} + + +//// [mappedTypes2.d.ts] +declare type Partial = { + [P in keyof T]?: T[P]; +}; +declare type Readonly = { + readonly [P in keyof T]: T[P]; +}; +declare type Pick = { + [P in K]: T[P]; +}; +declare type Record = { + [_ in K]: T; +}; +declare type Proxy = { + get(): T; + set(value: T): void; +}; +declare type Proxify = { + [P in keyof T]: Proxy; +}; +declare type DeepReadonly = { + readonly [P in keyof T]: DeepReadonly; +}; +declare function assign(obj: T, props: Partial): void; +declare function freeze(obj: T): Readonly; +declare function pick(obj: T, ...keys: K[]): Pick; +declare function mapObject(obj: Record, f: (x: T) => U): Record; +declare function proxify(obj: T): Proxify; +interface Shape { + name: string; + width: number; + height: number; + visible: boolean; +} +interface PartialShape { + name?: string; + width?: number; + height?: number; + visible?: boolean; +} +interface ReadonlyShape { + readonly name: string; + readonly width: number; + readonly height: number; + readonly visible: boolean; +} +declare function f0(s1: Shape, s2: Shape): void; +declare function f1(shape: Shape): void; +declare function f2(shape: Shape): void; +declare function f3(shape: Shape): void; +declare function f4(): void; +declare function f5(shape: Shape): void; +declare function f6(shape: DeepReadonly): void; diff --git a/tests/baselines/reference/mappedTypes2.symbols b/tests/baselines/reference/mappedTypes2.symbols new file mode 100644 index 0000000000000..bc823ed00818c --- /dev/null +++ b/tests/baselines/reference/mappedTypes2.symbols @@ -0,0 +1,331 @@ +=== tests/cases/conformance/types/mapped/mappedTypes2.ts === + +type Partial = { +>Partial : Symbol(Partial, Decl(mappedTypes2.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypes2.ts, 1, 13)) + + [P in keyof T]?: T[P]; +>P : Symbol(P, Decl(mappedTypes2.ts, 2, 5)) +>T : Symbol(T, Decl(mappedTypes2.ts, 1, 13)) +>T : Symbol(T, Decl(mappedTypes2.ts, 1, 13)) +>P : Symbol(P, Decl(mappedTypes2.ts, 2, 5)) + +}; + +type Readonly = { +>Readonly : Symbol(Readonly, Decl(mappedTypes2.ts, 3, 2)) +>T : Symbol(T, Decl(mappedTypes2.ts, 5, 14)) + + readonly [P in keyof T]: T[P]; +>P : Symbol(P, Decl(mappedTypes2.ts, 6, 14)) +>T : Symbol(T, Decl(mappedTypes2.ts, 5, 14)) +>T : Symbol(T, Decl(mappedTypes2.ts, 5, 14)) +>P : Symbol(P, Decl(mappedTypes2.ts, 6, 14)) + +}; + +type Pick = { +>Pick : Symbol(Pick, Decl(mappedTypes2.ts, 7, 2)) +>T : Symbol(T, Decl(mappedTypes2.ts, 9, 10)) +>K : Symbol(K, Decl(mappedTypes2.ts, 9, 12)) +>T : Symbol(T, Decl(mappedTypes2.ts, 9, 10)) + + [P in K]: T[P]; +>P : Symbol(P, Decl(mappedTypes2.ts, 10, 5)) +>K : Symbol(K, Decl(mappedTypes2.ts, 9, 12)) +>T : Symbol(T, Decl(mappedTypes2.ts, 9, 10)) +>P : Symbol(P, Decl(mappedTypes2.ts, 10, 5)) +} + +type Record = { +>Record : Symbol(Record, Decl(mappedTypes2.ts, 11, 1)) +>K : Symbol(K, Decl(mappedTypes2.ts, 13, 12)) +>T : Symbol(T, Decl(mappedTypes2.ts, 13, 38)) + + [_ in K]: T; +>_ : Symbol(_, Decl(mappedTypes2.ts, 14, 5)) +>K : Symbol(K, Decl(mappedTypes2.ts, 13, 12)) +>T : Symbol(T, Decl(mappedTypes2.ts, 13, 38)) +} + +type Proxy = { +>Proxy : Symbol(Proxy, Decl(mappedTypes2.ts, 15, 1)) +>T : Symbol(T, Decl(mappedTypes2.ts, 17, 11)) + + get(): T; +>get : Symbol(get, Decl(mappedTypes2.ts, 17, 17)) +>T : Symbol(T, Decl(mappedTypes2.ts, 17, 11)) + + set(value: T): void; +>set : Symbol(set, Decl(mappedTypes2.ts, 18, 13)) +>value : Symbol(value, Decl(mappedTypes2.ts, 19, 8)) +>T : Symbol(T, Decl(mappedTypes2.ts, 17, 11)) +} + +type Proxify = { +>Proxify : Symbol(Proxify, Decl(mappedTypes2.ts, 20, 1)) +>T : Symbol(T, Decl(mappedTypes2.ts, 22, 13)) + + [P in keyof T]: Proxy; +>P : Symbol(P, Decl(mappedTypes2.ts, 23, 5)) +>T : Symbol(T, Decl(mappedTypes2.ts, 22, 13)) +>Proxy : Symbol(Proxy, Decl(mappedTypes2.ts, 15, 1)) +>T : Symbol(T, Decl(mappedTypes2.ts, 22, 13)) +>P : Symbol(P, Decl(mappedTypes2.ts, 23, 5)) +} + +type DeepReadonly = { +>DeepReadonly : Symbol(DeepReadonly, Decl(mappedTypes2.ts, 24, 1)) +>T : Symbol(T, Decl(mappedTypes2.ts, 26, 18)) + + readonly [P in keyof T]: DeepReadonly; +>P : Symbol(P, Decl(mappedTypes2.ts, 27, 14)) +>T : Symbol(T, Decl(mappedTypes2.ts, 26, 18)) +>DeepReadonly : Symbol(DeepReadonly, Decl(mappedTypes2.ts, 24, 1)) +>T : Symbol(T, Decl(mappedTypes2.ts, 26, 18)) +>P : Symbol(P, Decl(mappedTypes2.ts, 27, 14)) + +}; + +declare function assign(obj: T, props: Partial): void; +>assign : Symbol(assign, Decl(mappedTypes2.ts, 28, 2)) +>T : Symbol(T, Decl(mappedTypes2.ts, 30, 24)) +>obj : Symbol(obj, Decl(mappedTypes2.ts, 30, 27)) +>T : Symbol(T, Decl(mappedTypes2.ts, 30, 24)) +>props : Symbol(props, Decl(mappedTypes2.ts, 30, 34)) +>Partial : Symbol(Partial, Decl(mappedTypes2.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypes2.ts, 30, 24)) + +declare function freeze(obj: T): Readonly; +>freeze : Symbol(freeze, Decl(mappedTypes2.ts, 30, 60)) +>T : Symbol(T, Decl(mappedTypes2.ts, 31, 24)) +>obj : Symbol(obj, Decl(mappedTypes2.ts, 31, 27)) +>T : Symbol(T, Decl(mappedTypes2.ts, 31, 24)) +>Readonly : Symbol(Readonly, Decl(mappedTypes2.ts, 3, 2)) +>T : Symbol(T, Decl(mappedTypes2.ts, 31, 24)) + +declare function pick(obj: T, ...keys: K[]): Pick; +>pick : Symbol(pick, Decl(mappedTypes2.ts, 31, 48)) +>T : Symbol(T, Decl(mappedTypes2.ts, 32, 22)) +>K : Symbol(K, Decl(mappedTypes2.ts, 32, 24)) +>T : Symbol(T, Decl(mappedTypes2.ts, 32, 22)) +>obj : Symbol(obj, Decl(mappedTypes2.ts, 32, 44)) +>T : Symbol(T, Decl(mappedTypes2.ts, 32, 22)) +>keys : Symbol(keys, Decl(mappedTypes2.ts, 32, 51)) +>K : Symbol(K, Decl(mappedTypes2.ts, 32, 24)) +>Pick : Symbol(Pick, Decl(mappedTypes2.ts, 7, 2)) +>T : Symbol(T, Decl(mappedTypes2.ts, 32, 22)) +>K : Symbol(K, Decl(mappedTypes2.ts, 32, 24)) + +declare function mapObject(obj: Record, f: (x: T) => U): Record; +>mapObject : Symbol(mapObject, Decl(mappedTypes2.ts, 32, 78)) +>K : Symbol(K, Decl(mappedTypes2.ts, 33, 27)) +>T : Symbol(T, Decl(mappedTypes2.ts, 33, 53)) +>U : Symbol(U, Decl(mappedTypes2.ts, 33, 56)) +>obj : Symbol(obj, Decl(mappedTypes2.ts, 33, 60)) +>Record : Symbol(Record, Decl(mappedTypes2.ts, 11, 1)) +>K : Symbol(K, Decl(mappedTypes2.ts, 33, 27)) +>T : Symbol(T, Decl(mappedTypes2.ts, 33, 53)) +>f : Symbol(f, Decl(mappedTypes2.ts, 33, 78)) +>x : Symbol(x, Decl(mappedTypes2.ts, 33, 83)) +>T : Symbol(T, Decl(mappedTypes2.ts, 33, 53)) +>U : Symbol(U, Decl(mappedTypes2.ts, 33, 56)) +>Record : Symbol(Record, Decl(mappedTypes2.ts, 11, 1)) +>K : Symbol(K, Decl(mappedTypes2.ts, 33, 27)) +>U : Symbol(U, Decl(mappedTypes2.ts, 33, 56)) + +declare function proxify(obj: T): Proxify; +>proxify : Symbol(proxify, Decl(mappedTypes2.ts, 33, 109)) +>T : Symbol(T, Decl(mappedTypes2.ts, 34, 25)) +>obj : Symbol(obj, Decl(mappedTypes2.ts, 34, 28)) +>T : Symbol(T, Decl(mappedTypes2.ts, 34, 25)) +>Proxify : Symbol(Proxify, Decl(mappedTypes2.ts, 20, 1)) +>T : Symbol(T, Decl(mappedTypes2.ts, 34, 25)) + +interface Shape { +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + name: string; +>name : Symbol(Shape.name, Decl(mappedTypes2.ts, 36, 17)) + + width: number; +>width : Symbol(Shape.width, Decl(mappedTypes2.ts, 37, 17)) + + height: number; +>height : Symbol(Shape.height, Decl(mappedTypes2.ts, 38, 18)) + + visible: boolean; +>visible : Symbol(Shape.visible, Decl(mappedTypes2.ts, 39, 19)) +} + +interface PartialShape { +>PartialShape : Symbol(PartialShape, Decl(mappedTypes2.ts, 41, 1)) + + name?: string; +>name : Symbol(PartialShape.name, Decl(mappedTypes2.ts, 43, 24)) + + width?: number; +>width : Symbol(PartialShape.width, Decl(mappedTypes2.ts, 44, 18)) + + height?: number; +>height : Symbol(PartialShape.height, Decl(mappedTypes2.ts, 45, 19)) + + visible?: boolean; +>visible : Symbol(PartialShape.visible, Decl(mappedTypes2.ts, 46, 20)) +} + +interface ReadonlyShape { +>ReadonlyShape : Symbol(ReadonlyShape, Decl(mappedTypes2.ts, 48, 1)) + + readonly name: string; +>name : Symbol(ReadonlyShape.name, Decl(mappedTypes2.ts, 50, 25)) + + readonly width: number; +>width : Symbol(ReadonlyShape.width, Decl(mappedTypes2.ts, 51, 26)) + + readonly height: number; +>height : Symbol(ReadonlyShape.height, Decl(mappedTypes2.ts, 52, 27)) + + readonly visible: boolean; +>visible : Symbol(ReadonlyShape.visible, Decl(mappedTypes2.ts, 53, 28)) +} + +function f0(s1: Shape, s2: Shape) { +>f0 : Symbol(f0, Decl(mappedTypes2.ts, 55, 1)) +>s1 : Symbol(s1, Decl(mappedTypes2.ts, 57, 12)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) +>s2 : Symbol(s2, Decl(mappedTypes2.ts, 57, 22)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + assign(s1, { name: "circle" }); +>assign : Symbol(assign, Decl(mappedTypes2.ts, 28, 2)) +>s1 : Symbol(s1, Decl(mappedTypes2.ts, 57, 12)) +>name : Symbol(name, Decl(mappedTypes2.ts, 58, 16)) + + assign(s2, { width: 10, height: 20 }); +>assign : Symbol(assign, Decl(mappedTypes2.ts, 28, 2)) +>s2 : Symbol(s2, Decl(mappedTypes2.ts, 57, 22)) +>width : Symbol(width, Decl(mappedTypes2.ts, 59, 16)) +>height : Symbol(height, Decl(mappedTypes2.ts, 59, 27)) +} + +function f1(shape: Shape) { +>f1 : Symbol(f1, Decl(mappedTypes2.ts, 60, 1)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 62, 12)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + var frozen: ReadonlyShape; +>frozen : Symbol(frozen, Decl(mappedTypes2.ts, 63, 7), Decl(mappedTypes2.ts, 64, 7), Decl(mappedTypes2.ts, 65, 7)) +>ReadonlyShape : Symbol(ReadonlyShape, Decl(mappedTypes2.ts, 48, 1)) + + var frozen: Readonly; +>frozen : Symbol(frozen, Decl(mappedTypes2.ts, 63, 7), Decl(mappedTypes2.ts, 64, 7), Decl(mappedTypes2.ts, 65, 7)) +>Readonly : Symbol(Readonly, Decl(mappedTypes2.ts, 3, 2)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + var frozen = freeze(shape); +>frozen : Symbol(frozen, Decl(mappedTypes2.ts, 63, 7), Decl(mappedTypes2.ts, 64, 7), Decl(mappedTypes2.ts, 65, 7)) +>freeze : Symbol(freeze, Decl(mappedTypes2.ts, 30, 60)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 62, 12)) +} + +function f2(shape: Shape) { +>f2 : Symbol(f2, Decl(mappedTypes2.ts, 66, 1)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 68, 12)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + var partial: PartialShape; +>partial : Symbol(partial, Decl(mappedTypes2.ts, 69, 7), Decl(mappedTypes2.ts, 70, 7), Decl(mappedTypes2.ts, 71, 7)) +>PartialShape : Symbol(PartialShape, Decl(mappedTypes2.ts, 41, 1)) + + var partial: Partial; +>partial : Symbol(partial, Decl(mappedTypes2.ts, 69, 7), Decl(mappedTypes2.ts, 70, 7), Decl(mappedTypes2.ts, 71, 7)) +>Partial : Symbol(Partial, Decl(mappedTypes2.ts, 0, 0)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + var partial: Partial = {}; +>partial : Symbol(partial, Decl(mappedTypes2.ts, 69, 7), Decl(mappedTypes2.ts, 70, 7), Decl(mappedTypes2.ts, 71, 7)) +>Partial : Symbol(Partial, Decl(mappedTypes2.ts, 0, 0)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) +} + +function f3(shape: Shape) { +>f3 : Symbol(f3, Decl(mappedTypes2.ts, 72, 1)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 74, 12)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + const x = pick(shape, "name", "visible"); // { name: string, visible: boolean } +>x : Symbol(x, Decl(mappedTypes2.ts, 75, 9)) +>pick : Symbol(pick, Decl(mappedTypes2.ts, 31, 48)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 74, 12)) +} + +function f4() { +>f4 : Symbol(f4, Decl(mappedTypes2.ts, 76, 1)) + + const rec = { foo: "hello", bar: "world", baz: "bye" }; +>rec : Symbol(rec, Decl(mappedTypes2.ts, 79, 9)) +>foo : Symbol(foo, Decl(mappedTypes2.ts, 79, 17)) +>bar : Symbol(bar, Decl(mappedTypes2.ts, 79, 31)) +>baz : Symbol(baz, Decl(mappedTypes2.ts, 79, 45)) + + const lengths = mapObject(rec, s => s.length); // { foo: number, bar: number, baz: number } +>lengths : Symbol(lengths, Decl(mappedTypes2.ts, 80, 9)) +>mapObject : Symbol(mapObject, Decl(mappedTypes2.ts, 32, 78)) +>rec : Symbol(rec, Decl(mappedTypes2.ts, 79, 9)) +>s : Symbol(s, Decl(mappedTypes2.ts, 80, 34)) +>s.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>s : Symbol(s, Decl(mappedTypes2.ts, 80, 34)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} + +function f5(shape: Shape) { +>f5 : Symbol(f5, Decl(mappedTypes2.ts, 81, 1)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 83, 12)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + const p = proxify(shape); +>p : Symbol(p, Decl(mappedTypes2.ts, 84, 9)) +>proxify : Symbol(proxify, Decl(mappedTypes2.ts, 33, 109)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 83, 12)) + + let name = p.name.get(); +>name : Symbol(name, Decl(mappedTypes2.ts, 85, 7)) +>p.name.get : Symbol(get, Decl(mappedTypes2.ts, 17, 17)) +>p.name : Symbol(name) +>p : Symbol(p, Decl(mappedTypes2.ts, 84, 9)) +>name : Symbol(name) +>get : Symbol(get, Decl(mappedTypes2.ts, 17, 17)) + + p.visible.set(false); +>p.visible.set : Symbol(set, Decl(mappedTypes2.ts, 18, 13)) +>p.visible : Symbol(visible) +>p : Symbol(p, Decl(mappedTypes2.ts, 84, 9)) +>visible : Symbol(visible) +>set : Symbol(set, Decl(mappedTypes2.ts, 18, 13)) +} + +function f6(shape: DeepReadonly) { +>f6 : Symbol(f6, Decl(mappedTypes2.ts, 87, 1)) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 89, 12)) +>DeepReadonly : Symbol(DeepReadonly, Decl(mappedTypes2.ts, 24, 1)) +>Shape : Symbol(Shape, Decl(mappedTypes2.ts, 34, 48)) + + let name = shape.name; // DeepReadonly +>name : Symbol(name, Decl(mappedTypes2.ts, 90, 7)) +>shape.name : Symbol(name) +>shape : Symbol(shape, Decl(mappedTypes2.ts, 89, 12)) +>name : Symbol(name) + + let length = name.length; // DeepReadonly +>length : Symbol(length, Decl(mappedTypes2.ts, 91, 7)) +>name.length : Symbol(length) +>name : Symbol(name, Decl(mappedTypes2.ts, 90, 7)) +>length : Symbol(length) + + let toString = length.toString; // DeepReadonly<(radix?: number) => string> +>toString : Symbol(toString, Decl(mappedTypes2.ts, 92, 7)) +>length.toString : Symbol(toString) +>length : Symbol(length, Decl(mappedTypes2.ts, 91, 7)) +>toString : Symbol(toString) +} diff --git a/tests/baselines/reference/mappedTypes2.types b/tests/baselines/reference/mappedTypes2.types new file mode 100644 index 0000000000000..e3b6764799319 --- /dev/null +++ b/tests/baselines/reference/mappedTypes2.types @@ -0,0 +1,353 @@ +=== tests/cases/conformance/types/mapped/mappedTypes2.ts === + +type Partial = { +>Partial : Partial +>T : T + + [P in keyof T]?: T[P]; +>P : P +>T : T +>T : T +>P : P + +}; + +type Readonly = { +>Readonly : Readonly +>T : T + + readonly [P in keyof T]: T[P]; +>P : P +>T : T +>T : T +>P : P + +}; + +type Pick = { +>Pick : Pick +>T : T +>K : K +>T : T + + [P in K]: T[P]; +>P : P +>K : K +>T : T +>P : P +} + +type Record = { +>Record : Record +>K : K +>T : T + + [_ in K]: T; +>_ : _ +>K : K +>T : T +} + +type Proxy = { +>Proxy : Proxy +>T : T + + get(): T; +>get : () => T +>T : T + + set(value: T): void; +>set : (value: T) => void +>value : T +>T : T +} + +type Proxify = { +>Proxify : Proxify +>T : T + + [P in keyof T]: Proxy; +>P : P +>T : T +>Proxy : Proxy +>T : T +>P : P +} + +type DeepReadonly = { +>DeepReadonly : DeepReadonly +>T : T + + readonly [P in keyof T]: DeepReadonly; +>P : P +>T : T +>DeepReadonly : DeepReadonly +>T : T +>P : P + +}; + +declare function assign(obj: T, props: Partial): void; +>assign : (obj: T, props: Partial) => void +>T : T +>obj : T +>T : T +>props : Partial +>Partial : Partial +>T : T + +declare function freeze(obj: T): Readonly; +>freeze : (obj: T) => Readonly +>T : T +>obj : T +>T : T +>Readonly : Readonly +>T : T + +declare function pick(obj: T, ...keys: K[]): Pick; +>pick : (obj: T, ...keys: K[]) => Pick +>T : T +>K : K +>T : T +>obj : T +>T : T +>keys : K[] +>K : K +>Pick : Pick +>T : T +>K : K + +declare function mapObject(obj: Record, f: (x: T) => U): Record; +>mapObject : (obj: Record, f: (x: T) => U) => Record +>K : K +>T : T +>U : U +>obj : Record +>Record : Record +>K : K +>T : T +>f : (x: T) => U +>x : T +>T : T +>U : U +>Record : Record +>K : K +>U : U + +declare function proxify(obj: T): Proxify; +>proxify : (obj: T) => Proxify +>T : T +>obj : T +>T : T +>Proxify : Proxify +>T : T + +interface Shape { +>Shape : Shape + + name: string; +>name : string + + width: number; +>width : number + + height: number; +>height : number + + visible: boolean; +>visible : boolean +} + +interface PartialShape { +>PartialShape : PartialShape + + name?: string; +>name : string | undefined + + width?: number; +>width : number | undefined + + height?: number; +>height : number | undefined + + visible?: boolean; +>visible : boolean | undefined +} + +interface ReadonlyShape { +>ReadonlyShape : ReadonlyShape + + readonly name: string; +>name : string + + readonly width: number; +>width : number + + readonly height: number; +>height : number + + readonly visible: boolean; +>visible : boolean +} + +function f0(s1: Shape, s2: Shape) { +>f0 : (s1: Shape, s2: Shape) => void +>s1 : Shape +>Shape : Shape +>s2 : Shape +>Shape : Shape + + assign(s1, { name: "circle" }); +>assign(s1, { name: "circle" }) : void +>assign : (obj: T, props: Partial) => void +>s1 : Shape +>{ name: "circle" } : { name: string; } +>name : string +>"circle" : "circle" + + assign(s2, { width: 10, height: 20 }); +>assign(s2, { width: 10, height: 20 }) : void +>assign : (obj: T, props: Partial) => void +>s2 : Shape +>{ width: 10, height: 20 } : { width: number; height: number; } +>width : number +>10 : 10 +>height : number +>20 : 20 +} + +function f1(shape: Shape) { +>f1 : (shape: Shape) => void +>shape : Shape +>Shape : Shape + + var frozen: ReadonlyShape; +>frozen : ReadonlyShape +>ReadonlyShape : ReadonlyShape + + var frozen: Readonly; +>frozen : ReadonlyShape +>Readonly : Readonly +>Shape : Shape + + var frozen = freeze(shape); +>frozen : ReadonlyShape +>freeze(shape) : Readonly +>freeze : (obj: T) => Readonly +>shape : Shape +} + +function f2(shape: Shape) { +>f2 : (shape: Shape) => void +>shape : Shape +>Shape : Shape + + var partial: PartialShape; +>partial : PartialShape +>PartialShape : PartialShape + + var partial: Partial; +>partial : PartialShape +>Partial : Partial +>Shape : Shape + + var partial: Partial = {}; +>partial : PartialShape +>Partial : Partial +>Shape : Shape +>{} : {} +} + +function f3(shape: Shape) { +>f3 : (shape: Shape) => void +>shape : Shape +>Shape : Shape + + const x = pick(shape, "name", "visible"); // { name: string, visible: boolean } +>x : Pick +>pick(shape, "name", "visible") : Pick +>pick : (obj: T, ...keys: K[]) => Pick +>shape : Shape +>"name" : "name" +>"visible" : "visible" +} + +function f4() { +>f4 : () => void + + const rec = { foo: "hello", bar: "world", baz: "bye" }; +>rec : { foo: string; bar: string; baz: string; } +>{ foo: "hello", bar: "world", baz: "bye" } : { foo: string; bar: string; baz: string; } +>foo : string +>"hello" : "hello" +>bar : string +>"world" : "world" +>baz : string +>"bye" : "bye" + + const lengths = mapObject(rec, s => s.length); // { foo: number, bar: number, baz: number } +>lengths : Record<"foo" | "bar" | "baz", number> +>mapObject(rec, s => s.length) : Record<"foo" | "bar" | "baz", number> +>mapObject : (obj: Record, f: (x: T) => U) => Record +>rec : { foo: string; bar: string; baz: string; } +>s => s.length : (s: string) => number +>s : string +>s.length : number +>s : string +>length : number +} + +function f5(shape: Shape) { +>f5 : (shape: Shape) => void +>shape : Shape +>Shape : Shape + + const p = proxify(shape); +>p : Proxify +>proxify(shape) : Proxify +>proxify : (obj: T) => Proxify +>shape : Shape + + let name = p.name.get(); +>name : string +>p.name.get() : string +>p.name.get : () => string +>p.name : Proxy +>p : Proxify +>name : Proxy +>get : () => string + + p.visible.set(false); +>p.visible.set(false) : void +>p.visible.set : (value: boolean) => void +>p.visible : Proxy +>p : Proxify +>visible : Proxy +>set : (value: boolean) => void +>false : false +} + +function f6(shape: DeepReadonly) { +>f6 : (shape: DeepReadonly) => void +>shape : DeepReadonly +>DeepReadonly : DeepReadonly +>Shape : Shape + + let name = shape.name; // DeepReadonly +>name : DeepReadonly +>shape.name : DeepReadonly +>shape : DeepReadonly +>name : DeepReadonly + + let length = name.length; // DeepReadonly +>length : DeepReadonly +>name.length : DeepReadonly +>name : DeepReadonly +>length : DeepReadonly + + let toString = length.toString; // DeepReadonly<(radix?: number) => string> +>toString : DeepReadonly<(radix?: number | undefined) => string> +>length.toString : DeepReadonly<(radix?: number | undefined) => string> +>length : DeepReadonly +>toString : DeepReadonly<(radix?: number | undefined) => string> +} diff --git a/tests/baselines/reference/mappedTypes3.js b/tests/baselines/reference/mappedTypes3.js new file mode 100644 index 0000000000000..712822e872888 --- /dev/null +++ b/tests/baselines/reference/mappedTypes3.js @@ -0,0 +1,84 @@ +//// [mappedTypes3.ts] + +class Box

{ + value: P; +} + +type Boxified = { + [K in keyof T]: Box; +} + +declare function boxify(obj: T): Boxified; +declare function unboxify(obj: Boxified): T; + +interface Bacon { + isPerfect: boolean; + weight: number; +} + +interface BoxifiedBacon { + isPerfect: Box; + weight: Box; +} + +function f1(b: Bacon) { + let bb = boxify(b); + let isPerfect = bb.isPerfect.value; + let weight = bb.weight.value; +} + +function f2(bb: Boxified) { + let b = unboxify(bb); // Infer Bacon for T + let bool = b.isPerfect; + let weight = b.weight; +} + +function f3(bb: BoxifiedBacon) { + let b = unboxify(bb); // Explicit type parameter required + let bool = b.isPerfect; + let weight = bb.weight; +} + +//// [mappedTypes3.js] +var Box = (function () { + function Box() { + } + return Box; +}()); +function f1(b) { + var bb = boxify(b); + var isPerfect = bb.isPerfect.value; + var weight = bb.weight.value; +} +function f2(bb) { + var b = unboxify(bb); // Infer Bacon for T + var bool = b.isPerfect; + var weight = b.weight; +} +function f3(bb) { + var b = unboxify(bb); // Explicit type parameter required + var bool = b.isPerfect; + var weight = bb.weight; +} + + +//// [mappedTypes3.d.ts] +declare class Box

{ + value: P; +} +declare type Boxified = { + [K in keyof T]: Box; +}; +declare function boxify(obj: T): Boxified; +declare function unboxify(obj: Boxified): T; +interface Bacon { + isPerfect: boolean; + weight: number; +} +interface BoxifiedBacon { + isPerfect: Box; + weight: Box; +} +declare function f1(b: Bacon): void; +declare function f2(bb: Boxified): void; +declare function f3(bb: BoxifiedBacon): void; diff --git a/tests/baselines/reference/mappedTypes3.symbols b/tests/baselines/reference/mappedTypes3.symbols new file mode 100644 index 0000000000000..3d909c8e532a1 --- /dev/null +++ b/tests/baselines/reference/mappedTypes3.symbols @@ -0,0 +1,135 @@ +=== tests/cases/conformance/types/mapped/mappedTypes3.ts === + +class Box

{ +>Box : Symbol(Box, Decl(mappedTypes3.ts, 0, 0)) +>P : Symbol(P, Decl(mappedTypes3.ts, 1, 10)) + + value: P; +>value : Symbol(Box.value, Decl(mappedTypes3.ts, 1, 14)) +>P : Symbol(P, Decl(mappedTypes3.ts, 1, 10)) +} + +type Boxified = { +>Boxified : Symbol(Boxified, Decl(mappedTypes3.ts, 3, 1)) +>T : Symbol(T, Decl(mappedTypes3.ts, 5, 14)) + + [K in keyof T]: Box; +>K : Symbol(K, Decl(mappedTypes3.ts, 6, 5)) +>T : Symbol(T, Decl(mappedTypes3.ts, 5, 14)) +>Box : Symbol(Box, Decl(mappedTypes3.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypes3.ts, 5, 14)) +>K : Symbol(K, Decl(mappedTypes3.ts, 6, 5)) +} + +declare function boxify(obj: T): Boxified; +>boxify : Symbol(boxify, Decl(mappedTypes3.ts, 7, 1)) +>T : Symbol(T, Decl(mappedTypes3.ts, 9, 24)) +>obj : Symbol(obj, Decl(mappedTypes3.ts, 9, 27)) +>T : Symbol(T, Decl(mappedTypes3.ts, 9, 24)) +>Boxified : Symbol(Boxified, Decl(mappedTypes3.ts, 3, 1)) +>T : Symbol(T, Decl(mappedTypes3.ts, 9, 24)) + +declare function unboxify(obj: Boxified): T; +>unboxify : Symbol(unboxify, Decl(mappedTypes3.ts, 9, 48)) +>T : Symbol(T, Decl(mappedTypes3.ts, 10, 26)) +>obj : Symbol(obj, Decl(mappedTypes3.ts, 10, 29)) +>Boxified : Symbol(Boxified, Decl(mappedTypes3.ts, 3, 1)) +>T : Symbol(T, Decl(mappedTypes3.ts, 10, 26)) +>T : Symbol(T, Decl(mappedTypes3.ts, 10, 26)) + +interface Bacon { +>Bacon : Symbol(Bacon, Decl(mappedTypes3.ts, 10, 50)) + + isPerfect: boolean; +>isPerfect : Symbol(Bacon.isPerfect, Decl(mappedTypes3.ts, 12, 17)) + + weight: number; +>weight : Symbol(Bacon.weight, Decl(mappedTypes3.ts, 13, 23)) +} + +interface BoxifiedBacon { +>BoxifiedBacon : Symbol(BoxifiedBacon, Decl(mappedTypes3.ts, 15, 1)) + + isPerfect: Box; +>isPerfect : Symbol(BoxifiedBacon.isPerfect, Decl(mappedTypes3.ts, 17, 25)) +>Box : Symbol(Box, Decl(mappedTypes3.ts, 0, 0)) + + weight: Box; +>weight : Symbol(BoxifiedBacon.weight, Decl(mappedTypes3.ts, 18, 28)) +>Box : Symbol(Box, Decl(mappedTypes3.ts, 0, 0)) +} + +function f1(b: Bacon) { +>f1 : Symbol(f1, Decl(mappedTypes3.ts, 20, 1)) +>b : Symbol(b, Decl(mappedTypes3.ts, 22, 12)) +>Bacon : Symbol(Bacon, Decl(mappedTypes3.ts, 10, 50)) + + let bb = boxify(b); +>bb : Symbol(bb, Decl(mappedTypes3.ts, 23, 7)) +>boxify : Symbol(boxify, Decl(mappedTypes3.ts, 7, 1)) +>b : Symbol(b, Decl(mappedTypes3.ts, 22, 12)) + + let isPerfect = bb.isPerfect.value; +>isPerfect : Symbol(isPerfect, Decl(mappedTypes3.ts, 24, 7)) +>bb.isPerfect.value : Symbol(Box.value, Decl(mappedTypes3.ts, 1, 14)) +>bb.isPerfect : Symbol(isPerfect) +>bb : Symbol(bb, Decl(mappedTypes3.ts, 23, 7)) +>isPerfect : Symbol(isPerfect) +>value : Symbol(Box.value, Decl(mappedTypes3.ts, 1, 14)) + + let weight = bb.weight.value; +>weight : Symbol(weight, Decl(mappedTypes3.ts, 25, 7)) +>bb.weight.value : Symbol(Box.value, Decl(mappedTypes3.ts, 1, 14)) +>bb.weight : Symbol(weight) +>bb : Symbol(bb, Decl(mappedTypes3.ts, 23, 7)) +>weight : Symbol(weight) +>value : Symbol(Box.value, Decl(mappedTypes3.ts, 1, 14)) +} + +function f2(bb: Boxified) { +>f2 : Symbol(f2, Decl(mappedTypes3.ts, 26, 1)) +>bb : Symbol(bb, Decl(mappedTypes3.ts, 28, 12)) +>Boxified : Symbol(Boxified, Decl(mappedTypes3.ts, 3, 1)) +>Bacon : Symbol(Bacon, Decl(mappedTypes3.ts, 10, 50)) + + let b = unboxify(bb); // Infer Bacon for T +>b : Symbol(b, Decl(mappedTypes3.ts, 29, 7)) +>unboxify : Symbol(unboxify, Decl(mappedTypes3.ts, 9, 48)) +>bb : Symbol(bb, Decl(mappedTypes3.ts, 28, 12)) + + let bool = b.isPerfect; +>bool : Symbol(bool, Decl(mappedTypes3.ts, 30, 7)) +>b.isPerfect : Symbol(Bacon.isPerfect, Decl(mappedTypes3.ts, 12, 17)) +>b : Symbol(b, Decl(mappedTypes3.ts, 29, 7)) +>isPerfect : Symbol(Bacon.isPerfect, Decl(mappedTypes3.ts, 12, 17)) + + let weight = b.weight; +>weight : Symbol(weight, Decl(mappedTypes3.ts, 31, 7)) +>b.weight : Symbol(Bacon.weight, Decl(mappedTypes3.ts, 13, 23)) +>b : Symbol(b, Decl(mappedTypes3.ts, 29, 7)) +>weight : Symbol(Bacon.weight, Decl(mappedTypes3.ts, 13, 23)) +} + +function f3(bb: BoxifiedBacon) { +>f3 : Symbol(f3, Decl(mappedTypes3.ts, 32, 1)) +>bb : Symbol(bb, Decl(mappedTypes3.ts, 34, 12)) +>BoxifiedBacon : Symbol(BoxifiedBacon, Decl(mappedTypes3.ts, 15, 1)) + + let b = unboxify(bb); // Explicit type parameter required +>b : Symbol(b, Decl(mappedTypes3.ts, 35, 7)) +>unboxify : Symbol(unboxify, Decl(mappedTypes3.ts, 9, 48)) +>Bacon : Symbol(Bacon, Decl(mappedTypes3.ts, 10, 50)) +>bb : Symbol(bb, Decl(mappedTypes3.ts, 34, 12)) + + let bool = b.isPerfect; +>bool : Symbol(bool, Decl(mappedTypes3.ts, 36, 7)) +>b.isPerfect : Symbol(Bacon.isPerfect, Decl(mappedTypes3.ts, 12, 17)) +>b : Symbol(b, Decl(mappedTypes3.ts, 35, 7)) +>isPerfect : Symbol(Bacon.isPerfect, Decl(mappedTypes3.ts, 12, 17)) + + let weight = bb.weight; +>weight : Symbol(weight, Decl(mappedTypes3.ts, 37, 7)) +>bb.weight : Symbol(BoxifiedBacon.weight, Decl(mappedTypes3.ts, 18, 28)) +>bb : Symbol(bb, Decl(mappedTypes3.ts, 34, 12)) +>weight : Symbol(BoxifiedBacon.weight, Decl(mappedTypes3.ts, 18, 28)) +} diff --git a/tests/baselines/reference/mappedTypes3.types b/tests/baselines/reference/mappedTypes3.types new file mode 100644 index 0000000000000..36471938d751c --- /dev/null +++ b/tests/baselines/reference/mappedTypes3.types @@ -0,0 +1,138 @@ +=== tests/cases/conformance/types/mapped/mappedTypes3.ts === + +class Box

{ +>Box : Box

+>P : P + + value: P; +>value : P +>P : P +} + +type Boxified = { +>Boxified : Boxified +>T : T + + [K in keyof T]: Box; +>K : K +>T : T +>Box : Box

+>T : T +>K : K +} + +declare function boxify(obj: T): Boxified; +>boxify : (obj: T) => Boxified +>T : T +>obj : T +>T : T +>Boxified : Boxified +>T : T + +declare function unboxify(obj: Boxified): T; +>unboxify : (obj: Boxified) => T +>T : T +>obj : Boxified +>Boxified : Boxified +>T : T +>T : T + +interface Bacon { +>Bacon : Bacon + + isPerfect: boolean; +>isPerfect : boolean + + weight: number; +>weight : number +} + +interface BoxifiedBacon { +>BoxifiedBacon : BoxifiedBacon + + isPerfect: Box; +>isPerfect : Box +>Box : Box

+ + weight: Box; +>weight : Box +>Box : Box

+} + +function f1(b: Bacon) { +>f1 : (b: Bacon) => void +>b : Bacon +>Bacon : Bacon + + let bb = boxify(b); +>bb : Boxified +>boxify(b) : Boxified +>boxify : (obj: T) => Boxified +>b : Bacon + + let isPerfect = bb.isPerfect.value; +>isPerfect : boolean +>bb.isPerfect.value : boolean +>bb.isPerfect : Box +>bb : Boxified +>isPerfect : Box +>value : boolean + + let weight = bb.weight.value; +>weight : number +>bb.weight.value : number +>bb.weight : Box +>bb : Boxified +>weight : Box +>value : number +} + +function f2(bb: Boxified) { +>f2 : (bb: Boxified) => void +>bb : Boxified +>Boxified : Boxified +>Bacon : Bacon + + let b = unboxify(bb); // Infer Bacon for T +>b : Bacon +>unboxify(bb) : Bacon +>unboxify : (obj: Boxified) => T +>bb : Boxified + + let bool = b.isPerfect; +>bool : boolean +>b.isPerfect : boolean +>b : Bacon +>isPerfect : boolean + + let weight = b.weight; +>weight : number +>b.weight : number +>b : Bacon +>weight : number +} + +function f3(bb: BoxifiedBacon) { +>f3 : (bb: BoxifiedBacon) => void +>bb : BoxifiedBacon +>BoxifiedBacon : BoxifiedBacon + + let b = unboxify(bb); // Explicit type parameter required +>b : Bacon +>unboxify(bb) : Bacon +>unboxify : (obj: Boxified) => T +>Bacon : Bacon +>bb : BoxifiedBacon + + let bool = b.isPerfect; +>bool : boolean +>b.isPerfect : boolean +>b : Bacon +>isPerfect : boolean + + let weight = bb.weight; +>weight : Box +>bb.weight : Box +>bb : BoxifiedBacon +>weight : Box +} diff --git a/tests/cases/conformance/types/mapped/mappedTypes1.ts b/tests/cases/conformance/types/mapped/mappedTypes1.ts new file mode 100644 index 0000000000000..f57a14a2e10bd --- /dev/null +++ b/tests/cases/conformance/types/mapped/mappedTypes1.ts @@ -0,0 +1,35 @@ +// @strictNullChecks: true +// @declaration: true + +type Item = { a: string, b: number, c: boolean }; + +type T00 = { [P in "x" | "y"]: number }; +type T01 = { [P in "x" | "y"]: P }; +type T02 = { [P in "a" | "b"]: Item[P]; } +type T03 = { [P in keyof Item]: Date }; + +type T10 = { [P in keyof Item]: Item[P] }; +type T11 = { [P in keyof Item]?: Item[P] }; +type T12 = { readonly [P in keyof Item]: Item[P] }; +type T13 = { readonly [P in keyof Item]?: Item[P] }; + +type T20 = { [P in keyof Item]: Item[P] | null }; +type T21 = { [P in keyof Item]: Array }; + +type T30 = { [P in keyof any]: void }; +type T31 = { [P in keyof string]: void }; +type T32 = { [P in keyof number]: void }; +type T33 = { [P in keyof boolean]: void }; +type T34 = { [P in keyof undefined]: void }; +type T35 = { [P in keyof null]: void }; +type T36 = { [P in keyof void]: void }; +type T37 = { [P in keyof symbol]: void }; +type T38 = { [P in keyof never]: void }; + +declare function f1(): { [P in keyof T1]: void }; +declare function f2(): { [P in keyof T1]: void }; +declare function f3(): { [P in keyof T1]: void }; + +let x1 = f1(); +let x2 = f2(); +let x3 = f3(); \ No newline at end of file diff --git a/tests/cases/conformance/types/mapped/mappedTypes2.ts b/tests/cases/conformance/types/mapped/mappedTypes2.ts new file mode 100644 index 0000000000000..84bffe2ea1773 --- /dev/null +++ b/tests/cases/conformance/types/mapped/mappedTypes2.ts @@ -0,0 +1,96 @@ +// @strictNullChecks: true +// @declaration: true + +type Partial = { + [P in keyof T]?: T[P]; +}; + +type Readonly = { + readonly [P in keyof T]: T[P]; +}; + +type Pick = { + [P in K]: T[P]; +} + +type Record = { + [_ in K]: T; +} + +type Proxy = { + get(): T; + set(value: T): void; +} + +type Proxify = { + [P in keyof T]: Proxy; +} + +type DeepReadonly = { + readonly [P in keyof T]: DeepReadonly; +}; + +declare function assign(obj: T, props: Partial): void; +declare function freeze(obj: T): Readonly; +declare function pick(obj: T, ...keys: K[]): Pick; +declare function mapObject(obj: Record, f: (x: T) => U): Record; +declare function proxify(obj: T): Proxify; + +interface Shape { + name: string; + width: number; + height: number; + visible: boolean; +} + +interface PartialShape { + name?: string; + width?: number; + height?: number; + visible?: boolean; +} + +interface ReadonlyShape { + readonly name: string; + readonly width: number; + readonly height: number; + readonly visible: boolean; +} + +function f0(s1: Shape, s2: Shape) { + assign(s1, { name: "circle" }); + assign(s2, { width: 10, height: 20 }); +} + +function f1(shape: Shape) { + var frozen: ReadonlyShape; + var frozen: Readonly; + var frozen = freeze(shape); +} + +function f2(shape: Shape) { + var partial: PartialShape; + var partial: Partial; + var partial: Partial = {}; +} + +function f3(shape: Shape) { + const x = pick(shape, "name", "visible"); // { name: string, visible: boolean } +} + +function f4() { + const rec = { foo: "hello", bar: "world", baz: "bye" }; + const lengths = mapObject(rec, s => s.length); // { foo: number, bar: number, baz: number } +} + +function f5(shape: Shape) { + const p = proxify(shape); + let name = p.name.get(); + p.visible.set(false); +} + +function f6(shape: DeepReadonly) { + let name = shape.name; // DeepReadonly + let length = name.length; // DeepReadonly + let toString = length.toString; // DeepReadonly<(radix?: number) => string> +} \ No newline at end of file diff --git a/tests/cases/conformance/types/mapped/mappedTypes3.ts b/tests/cases/conformance/types/mapped/mappedTypes3.ts new file mode 100644 index 0000000000000..bf5a4399b0dd5 --- /dev/null +++ b/tests/cases/conformance/types/mapped/mappedTypes3.ts @@ -0,0 +1,40 @@ +// @declaration: true + +class Box

{ + value: P; +} + +type Boxified = { + [K in keyof T]: Box; +} + +declare function boxify(obj: T): Boxified; +declare function unboxify(obj: Boxified): T; + +interface Bacon { + isPerfect: boolean; + weight: number; +} + +interface BoxifiedBacon { + isPerfect: Box; + weight: Box; +} + +function f1(b: Bacon) { + let bb = boxify(b); + let isPerfect = bb.isPerfect.value; + let weight = bb.weight.value; +} + +function f2(bb: Boxified) { + let b = unboxify(bb); // Infer Bacon for T + let bool = b.isPerfect; + let weight = b.weight; +} + +function f3(bb: BoxifiedBacon) { + let b = unboxify(bb); // Explicit type parameter required + let bool = b.isPerfect; + let weight = bb.weight; +} \ No newline at end of file From ca3f79783277beae5b06954cc7bec9463cc6789c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 11 Nov 2016 11:00:09 -0800 Subject: [PATCH 24/28] More tests --- .../types/mapped/mappedTypeErrors.ts | 62 +++++++++++++++++++ .../conformance/types/mapped/mappedTypes1.ts | 9 +++ 2 files changed, 71 insertions(+) create mode 100644 tests/cases/conformance/types/mapped/mappedTypeErrors.ts diff --git a/tests/cases/conformance/types/mapped/mappedTypeErrors.ts b/tests/cases/conformance/types/mapped/mappedTypeErrors.ts new file mode 100644 index 0000000000000..4017951f7c217 --- /dev/null +++ b/tests/cases/conformance/types/mapped/mappedTypeErrors.ts @@ -0,0 +1,62 @@ +// @strictNullChecks: true +// @declaration: true + +type Partial = { + [P in keyof T]?: T[P]; +}; + +type Readonly = { + readonly [P in keyof T]: T[P]; +}; + +type Pick = { + [P in K]: T[P]; +} + +type Record = { + [_ in K]: T; +} + +interface Shape { + name: string; + width: number; + height: number; + visible: boolean; +} + +interface Named { + name: string; +} + +interface Point { + x: number; + y: number; +} + +type T00 = { [P in P]: string }; // Error +type T01 = { [P in Date]: number }; // Error +type T02 = Record; // Error + +type T10 = Pick; +type T11 = Pick; // Error +type T12 = Pick; // Error +type T13 = Pick; +type T14 = Pick; // Error +type T15 = Pick; +type T16 = Pick; + +function f1(x: T) { + let y: Pick; // Error +} + +function f2(x: T) { + let y: Pick; // Error +} + +function f3(x: T) { + let y: Pick; +} + +function f4(x: T) { + let y: Pick; +} \ No newline at end of file diff --git a/tests/cases/conformance/types/mapped/mappedTypes1.ts b/tests/cases/conformance/types/mapped/mappedTypes1.ts index f57a14a2e10bd..bfc68aaa59d20 100644 --- a/tests/cases/conformance/types/mapped/mappedTypes1.ts +++ b/tests/cases/conformance/types/mapped/mappedTypes1.ts @@ -26,6 +26,15 @@ type T36 = { [P in keyof void]: void }; type T37 = { [P in keyof symbol]: void }; type T38 = { [P in keyof never]: void }; +type T40 = { [P in string]: void }; +type T41 = { [P in number]: void }; +type T42 = { [P in string | number]: void }; +type T43 = { [P in "a" | "b" | 0 | 1]: void }; +type T44 = { [P in "a" | "b" | "0" | "1"]: void }; +type T45 = { [P in "a" | "b" | "0" | "1" | 0 | 1]: void }; +type T46 = { [P in number | "a" | "b" | 0 | 1]: void }; +type T47 = { [P in string | number | "a" | "b" | 0 | 1]: void }; + declare function f1(): { [P in keyof T1]: void }; declare function f2(): { [P in keyof T1]: void }; declare function f3(): { [P in keyof T1]: void }; From 5028a44e562c27636c6ad98addc410925e002709 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 11 Nov 2016 11:00:24 -0800 Subject: [PATCH 25/28] Accept new baselines --- .../reference/mappedTypeErrors.errors.txt | 110 ++++++++++++++++ tests/baselines/reference/mappedTypeErrors.js | 121 ++++++++++++++++++ tests/baselines/reference/mappedTypes1.js | 33 +++++ .../baselines/reference/mappedTypes1.symbols | 68 +++++++--- tests/baselines/reference/mappedTypes1.types | 32 +++++ 5 files changed, 346 insertions(+), 18 deletions(-) create mode 100644 tests/baselines/reference/mappedTypeErrors.errors.txt create mode 100644 tests/baselines/reference/mappedTypeErrors.js diff --git a/tests/baselines/reference/mappedTypeErrors.errors.txt b/tests/baselines/reference/mappedTypeErrors.errors.txt new file mode 100644 index 0000000000000..724ab902fc3fe --- /dev/null +++ b/tests/baselines/reference/mappedTypeErrors.errors.txt @@ -0,0 +1,110 @@ +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(34,20): error TS2313: Type parameter 'P' has a circular constraint. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(35,20): error TS2322: Type 'Date' is not assignable to type 'string | number'. + Type 'Date' is not assignable to type 'number'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(36,19): error TS2344: Type 'Date' does not satisfy the constraint 'string | number'. + Type 'Date' is not assignable to type 'number'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(39,24): error TS2344: Type '"foo"' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(40,24): error TS2344: Type '"name" | "foo"' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. + Type '"foo"' is not assignable to type '"name" | "width" | "height" | "visible"'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(42,24): error TS2344: Type '"x" | "y"' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. + Type '"x"' is not assignable to type '"name" | "width" | "height" | "visible"'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(44,24): error TS2344: Type 'undefined' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(47,24): error TS2344: Type 'T' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. + Type 'T' is not assignable to type '"visible"'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(51,24): error TS2344: Type 'T' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. + Type 'string | number' is not assignable to type '"name" | "width" | "height" | "visible"'. + Type 'string' is not assignable to type '"name" | "width" | "height" | "visible"'. + Type 'T' is not assignable to type '"visible"'. + Type 'string | number' is not assignable to type '"visible"'. + Type 'string' is not assignable to type '"visible"'. + + +==== tests/cases/conformance/types/mapped/mappedTypeErrors.ts (9 errors) ==== + + type Partial = { + [P in keyof T]?: T[P]; + }; + + type Readonly = { + readonly [P in keyof T]: T[P]; + }; + + type Pick = { + [P in K]: T[P]; + } + + type Record = { + [_ in K]: T; + } + + interface Shape { + name: string; + width: number; + height: number; + visible: boolean; + } + + interface Named { + name: string; + } + + interface Point { + x: number; + y: number; + } + + type T00 = { [P in P]: string }; // Error + ~ +!!! error TS2313: Type parameter 'P' has a circular constraint. + type T01 = { [P in Date]: number }; // Error + ~~~~ +!!! error TS2322: Type 'Date' is not assignable to type 'string | number'. +!!! error TS2322: Type 'Date' is not assignable to type 'number'. + type T02 = Record; // Error + ~~~~ +!!! error TS2344: Type 'Date' does not satisfy the constraint 'string | number'. +!!! error TS2344: Type 'Date' is not assignable to type 'number'. + + type T10 = Pick; + type T11 = Pick; // Error + ~~~~~ +!!! error TS2344: Type '"foo"' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. + type T12 = Pick; // Error + ~~~~~~~~~~~~~~ +!!! error TS2344: Type '"name" | "foo"' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. +!!! error TS2344: Type '"foo"' is not assignable to type '"name" | "width" | "height" | "visible"'. + type T13 = Pick; + type T14 = Pick; // Error + ~~~~~~~~~~~ +!!! error TS2344: Type '"x" | "y"' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. +!!! error TS2344: Type '"x"' is not assignable to type '"name" | "width" | "height" | "visible"'. + type T15 = Pick; + type T16 = Pick; + ~~~~~~~~~ +!!! error TS2344: Type 'undefined' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. + + function f1(x: T) { + let y: Pick; // Error + ~ +!!! error TS2344: Type 'T' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. +!!! error TS2344: Type 'T' is not assignable to type '"visible"'. + } + + function f2(x: T) { + let y: Pick; // Error + ~ +!!! error TS2344: Type 'T' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. +!!! error TS2344: Type 'string | number' is not assignable to type '"name" | "width" | "height" | "visible"'. +!!! error TS2344: Type 'string' is not assignable to type '"name" | "width" | "height" | "visible"'. +!!! error TS2344: Type 'T' is not assignable to type '"visible"'. +!!! error TS2344: Type 'string | number' is not assignable to type '"visible"'. +!!! error TS2344: Type 'string' is not assignable to type '"visible"'. + } + + function f3(x: T) { + let y: Pick; + } + + function f4(x: T) { + let y: Pick; + } \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeErrors.js b/tests/baselines/reference/mappedTypeErrors.js new file mode 100644 index 0000000000000..f717f0d02f390 --- /dev/null +++ b/tests/baselines/reference/mappedTypeErrors.js @@ -0,0 +1,121 @@ +//// [mappedTypeErrors.ts] + +type Partial = { + [P in keyof T]?: T[P]; +}; + +type Readonly = { + readonly [P in keyof T]: T[P]; +}; + +type Pick = { + [P in K]: T[P]; +} + +type Record = { + [_ in K]: T; +} + +interface Shape { + name: string; + width: number; + height: number; + visible: boolean; +} + +interface Named { + name: string; +} + +interface Point { + x: number; + y: number; +} + +type T00 = { [P in P]: string }; // Error +type T01 = { [P in Date]: number }; // Error +type T02 = Record; // Error + +type T10 = Pick; +type T11 = Pick; // Error +type T12 = Pick; // Error +type T13 = Pick; +type T14 = Pick; // Error +type T15 = Pick; +type T16 = Pick; + +function f1(x: T) { + let y: Pick; // Error +} + +function f2(x: T) { + let y: Pick; // Error +} + +function f3(x: T) { + let y: Pick; +} + +function f4(x: T) { + let y: Pick; +} + +//// [mappedTypeErrors.js] +function f1(x) { + var y; // Error +} +function f2(x) { + var y; // Error +} +function f3(x) { + var y; +} +function f4(x) { + var y; +} + + +//// [mappedTypeErrors.d.ts] +declare type Partial = { + [P in keyof T]?: T[P]; +}; +declare type Readonly = { + readonly [P in keyof T]: T[P]; +}; +declare type Pick = { + [P in K]: T[P]; +}; +declare type Record = { + [_ in K]: T; +}; +interface Shape { + name: string; + width: number; + height: number; + visible: boolean; +} +interface Named { + name: string; +} +interface Point { + x: number; + y: number; +} +declare type T00 = { + [P in P]: string; +}; +declare type T01 = { + [P in Date]: number; +}; +declare type T02 = Record; +declare type T10 = Pick; +declare type T11 = Pick; +declare type T12 = Pick; +declare type T13 = Pick; +declare type T14 = Pick; +declare type T15 = Pick; +declare type T16 = Pick; +declare function f1(x: T): void; +declare function f2(x: T): void; +declare function f3(x: T): void; +declare function f4(x: T): void; diff --git a/tests/baselines/reference/mappedTypes1.js b/tests/baselines/reference/mappedTypes1.js index df03a0229662c..71a8d5abb67e2 100644 --- a/tests/baselines/reference/mappedTypes1.js +++ b/tests/baselines/reference/mappedTypes1.js @@ -25,6 +25,15 @@ type T36 = { [P in keyof void]: void }; type T37 = { [P in keyof symbol]: void }; type T38 = { [P in keyof never]: void }; +type T40 = { [P in string]: void }; +type T41 = { [P in number]: void }; +type T42 = { [P in string | number]: void }; +type T43 = { [P in "a" | "b" | 0 | 1]: void }; +type T44 = { [P in "a" | "b" | "0" | "1"]: void }; +type T45 = { [P in "a" | "b" | "0" | "1" | 0 | 1]: void }; +type T46 = { [P in number | "a" | "b" | 0 | 1]: void }; +type T47 = { [P in string | number | "a" | "b" | 0 | 1]: void }; + declare function f1(): { [P in keyof T1]: void }; declare function f2(): { [P in keyof T1]: void }; declare function f3(): { [P in keyof T1]: void }; @@ -102,6 +111,30 @@ declare type T37 = { declare type T38 = { [P in keyof never]: void; }; +declare type T40 = { + [P in string]: void; +}; +declare type T41 = { + [P in number]: void; +}; +declare type T42 = { + [P in string | number]: void; +}; +declare type T43 = { + [P in "a" | "b" | 0 | 1]: void; +}; +declare type T44 = { + [P in "a" | "b" | "0" | "1"]: void; +}; +declare type T45 = { + [P in "a" | "b" | "0" | "1" | 0 | 1]: void; +}; +declare type T46 = { + [P in number | "a" | "b" | 0 | 1]: void; +}; +declare type T47 = { + [P in string | number | "a" | "b" | 0 | 1]: void; +}; declare function f1(): { [P in keyof T1]: void; }; diff --git a/tests/baselines/reference/mappedTypes1.symbols b/tests/baselines/reference/mappedTypes1.symbols index 65732a4be11d9..908fbb3b0e1ae 100644 --- a/tests/baselines/reference/mappedTypes1.symbols +++ b/tests/baselines/reference/mappedTypes1.symbols @@ -106,33 +106,65 @@ type T38 = { [P in keyof never]: void }; >T38 : Symbol(T38, Decl(mappedTypes1.ts, 23, 41)) >P : Symbol(P, Decl(mappedTypes1.ts, 24, 14)) +type T40 = { [P in string]: void }; +>T40 : Symbol(T40, Decl(mappedTypes1.ts, 24, 40)) +>P : Symbol(P, Decl(mappedTypes1.ts, 26, 14)) + +type T41 = { [P in number]: void }; +>T41 : Symbol(T41, Decl(mappedTypes1.ts, 26, 35)) +>P : Symbol(P, Decl(mappedTypes1.ts, 27, 14)) + +type T42 = { [P in string | number]: void }; +>T42 : Symbol(T42, Decl(mappedTypes1.ts, 27, 35)) +>P : Symbol(P, Decl(mappedTypes1.ts, 28, 14)) + +type T43 = { [P in "a" | "b" | 0 | 1]: void }; +>T43 : Symbol(T43, Decl(mappedTypes1.ts, 28, 44)) +>P : Symbol(P, Decl(mappedTypes1.ts, 29, 14)) + +type T44 = { [P in "a" | "b" | "0" | "1"]: void }; +>T44 : Symbol(T44, Decl(mappedTypes1.ts, 29, 46)) +>P : Symbol(P, Decl(mappedTypes1.ts, 30, 14)) + +type T45 = { [P in "a" | "b" | "0" | "1" | 0 | 1]: void }; +>T45 : Symbol(T45, Decl(mappedTypes1.ts, 30, 50)) +>P : Symbol(P, Decl(mappedTypes1.ts, 31, 14)) + +type T46 = { [P in number | "a" | "b" | 0 | 1]: void }; +>T46 : Symbol(T46, Decl(mappedTypes1.ts, 31, 58)) +>P : Symbol(P, Decl(mappedTypes1.ts, 32, 14)) + +type T47 = { [P in string | number | "a" | "b" | 0 | 1]: void }; +>T47 : Symbol(T47, Decl(mappedTypes1.ts, 32, 55)) +>P : Symbol(P, Decl(mappedTypes1.ts, 33, 14)) + declare function f1(): { [P in keyof T1]: void }; ->f1 : Symbol(f1, Decl(mappedTypes1.ts, 24, 40)) ->T1 : Symbol(T1, Decl(mappedTypes1.ts, 26, 20)) ->P : Symbol(P, Decl(mappedTypes1.ts, 26, 30)) ->T1 : Symbol(T1, Decl(mappedTypes1.ts, 26, 20)) +>f1 : Symbol(f1, Decl(mappedTypes1.ts, 33, 64)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 35, 20)) +>P : Symbol(P, Decl(mappedTypes1.ts, 35, 30)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 35, 20)) declare function f2(): { [P in keyof T1]: void }; ->f2 : Symbol(f2, Decl(mappedTypes1.ts, 26, 53)) ->T1 : Symbol(T1, Decl(mappedTypes1.ts, 27, 20)) ->P : Symbol(P, Decl(mappedTypes1.ts, 27, 45)) ->T1 : Symbol(T1, Decl(mappedTypes1.ts, 27, 20)) +>f2 : Symbol(f2, Decl(mappedTypes1.ts, 35, 53)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 36, 20)) +>P : Symbol(P, Decl(mappedTypes1.ts, 36, 45)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 36, 20)) declare function f3(): { [P in keyof T1]: void }; ->f3 : Symbol(f3, Decl(mappedTypes1.ts, 27, 68)) ->T1 : Symbol(T1, Decl(mappedTypes1.ts, 28, 20)) ->P : Symbol(P, Decl(mappedTypes1.ts, 28, 45)) ->T1 : Symbol(T1, Decl(mappedTypes1.ts, 28, 20)) +>f3 : Symbol(f3, Decl(mappedTypes1.ts, 36, 68)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 37, 20)) +>P : Symbol(P, Decl(mappedTypes1.ts, 37, 45)) +>T1 : Symbol(T1, Decl(mappedTypes1.ts, 37, 20)) let x1 = f1(); ->x1 : Symbol(x1, Decl(mappedTypes1.ts, 30, 3)) ->f1 : Symbol(f1, Decl(mappedTypes1.ts, 24, 40)) +>x1 : Symbol(x1, Decl(mappedTypes1.ts, 39, 3)) +>f1 : Symbol(f1, Decl(mappedTypes1.ts, 33, 64)) let x2 = f2(); ->x2 : Symbol(x2, Decl(mappedTypes1.ts, 31, 3)) ->f2 : Symbol(f2, Decl(mappedTypes1.ts, 26, 53)) +>x2 : Symbol(x2, Decl(mappedTypes1.ts, 40, 3)) +>f2 : Symbol(f2, Decl(mappedTypes1.ts, 35, 53)) let x3 = f3(); ->x3 : Symbol(x3, Decl(mappedTypes1.ts, 32, 3)) ->f3 : Symbol(f3, Decl(mappedTypes1.ts, 27, 68)) +>x3 : Symbol(x3, Decl(mappedTypes1.ts, 41, 3)) +>f3 : Symbol(f3, Decl(mappedTypes1.ts, 36, 68)) diff --git a/tests/baselines/reference/mappedTypes1.types b/tests/baselines/reference/mappedTypes1.types index 222291b008b59..06aeaa8962130 100644 --- a/tests/baselines/reference/mappedTypes1.types +++ b/tests/baselines/reference/mappedTypes1.types @@ -108,6 +108,38 @@ type T38 = { [P in keyof never]: void }; >T38 : T38 >P : P +type T40 = { [P in string]: void }; +>T40 : T40 +>P : P + +type T41 = { [P in number]: void }; +>T41 : T41 +>P : P + +type T42 = { [P in string | number]: void }; +>T42 : T42 +>P : P + +type T43 = { [P in "a" | "b" | 0 | 1]: void }; +>T43 : T43 +>P : P + +type T44 = { [P in "a" | "b" | "0" | "1"]: void }; +>T44 : T44 +>P : P + +type T45 = { [P in "a" | "b" | "0" | "1" | 0 | 1]: void }; +>T45 : T45 +>P : P + +type T46 = { [P in number | "a" | "b" | 0 | 1]: void }; +>T46 : T46 +>P : P + +type T47 = { [P in string | number | "a" | "b" | 0 | 1]: void }; +>T47 : T47 +>P : P + declare function f1(): { [P in keyof T1]: void }; >f1 : () => { [P in keyof T1]: void; } >T1 : T1 From 9ac7667d5c5c9f7ef6ea7e294043d27b1da32a73 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 12 Nov 2016 08:55:23 -0800 Subject: [PATCH 26/28] Address CR feedback --- src/compiler/checker.ts | 30 +++++++++++-------- .../types/mapped/mappedTypeErrors.ts | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 01ea222721737..d75ae6a8cdc49 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4484,33 +4484,33 @@ namespace ts { } } - function forEachType(type: Type, f: (t: Type) => T): T { - return type.flags & TypeFlags.Union ? forEach((type).types, f) : f(type); - } - - // { [P in K]: T } - // Get apparent type of K - // If apparent type is a 'keyof T', get apparent type of T - // For each constituent literal type U - // create mapper from P to U - // instantiate T using mapper - // if U is string or number, create index signature with instantiated type - // otherwise create property with name from U and instantiated type + /** Resolve the members of a mapped type { [P in K]: T } */ function resolveMappedTypeMembers(type: MappedType) { const members: SymbolTable = createMap(); let stringIndexInfo: IndexInfo; let numberIndexInfo: IndexInfo; + // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, + // and T as the template type. const typeParameter = getTypeParameterFromMappedType(type); const constraintType = getConstraintTypeFromMappedType(type); const templateType = getTemplateTypeFromMappedType(type); const isReadonly = !!type.declaration.readonlyToken; const isOptional = !!type.declaration.questionToken; + // First, if the constraint type is a type parameter, obtain the base constraint. Then, + // if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X. + // Finally, iterate over the constituents of the resulting iteration type. const keyType = constraintType.flags & TypeFlags.TypeParameter ? getApparentType(constraintType) : constraintType; const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((keyType).type)) : keyType; forEachType(iterationType, t => { + // Create a mapper from T to the current iteration type constituent. Then, if the + // mapped type is itself an instantiated type, combine the iteration mapper with the + // instantiation mapper. const iterationMapper = createUnaryTypeMapper(typeParameter, t); const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper; const propType = instantiateType(templateType, templateMapper); + // If the current iteration type constituent is a literal type, create a property. + // Otherwise, for type string create a string index signature and for type number + // create a numeric index signature. if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) { const propName = (t).text; const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName); @@ -4525,6 +4525,8 @@ namespace ts { numberIndexInfo = createIndexInfo(propType, isReadonly); } }); + // If we created both a string and a numeric string index signature, and if the two index + // signatures have identical types, discard the redundant numeric index signature. if (stringIndexInfo && numberIndexInfo && isTypeIdenticalTo(stringIndexInfo.type, numberIndexInfo.type)) { numberIndexInfo = undefined; } @@ -9060,6 +9062,10 @@ namespace ts { return containsType(target.types, source); } + function forEachType(type: Type, f: (t: Type) => T): T { + return type.flags & TypeFlags.Union ? forEach((type).types, f) : f(type); + } + function filterType(type: Type, f: (t: Type) => boolean): Type { if (type.flags & TypeFlags.Union) { const types = (type).types; diff --git a/tests/cases/conformance/types/mapped/mappedTypeErrors.ts b/tests/cases/conformance/types/mapped/mappedTypeErrors.ts index 4017951f7c217..fce80872185c7 100644 --- a/tests/cases/conformance/types/mapped/mappedTypeErrors.ts +++ b/tests/cases/conformance/types/mapped/mappedTypeErrors.ts @@ -43,7 +43,7 @@ type T12 = Pick; // Error type T13 = Pick; type T14 = Pick; // Error type T15 = Pick; -type T16 = Pick; +type T16 = Pick; // Error function f1(x: T) { let y: Pick; // Error From 6ceab7bce78f154c650a6cbbef465b52f5912e92 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 12 Nov 2016 09:20:30 -0800 Subject: [PATCH 27/28] Accept new baselines --- tests/baselines/reference/mappedTypeErrors.errors.txt | 2 +- tests/baselines/reference/mappedTypeErrors.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/mappedTypeErrors.errors.txt b/tests/baselines/reference/mappedTypeErrors.errors.txt index 724ab902fc3fe..4efcc579ba8ee 100644 --- a/tests/baselines/reference/mappedTypeErrors.errors.txt +++ b/tests/baselines/reference/mappedTypeErrors.errors.txt @@ -79,7 +79,7 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(51,24): error TS2344: T !!! error TS2344: Type '"x" | "y"' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. !!! error TS2344: Type '"x"' is not assignable to type '"name" | "width" | "height" | "visible"'. type T15 = Pick; - type T16 = Pick; + type T16 = Pick; // Error ~~~~~~~~~ !!! error TS2344: Type 'undefined' does not satisfy the constraint '"name" | "width" | "height" | "visible"'. diff --git a/tests/baselines/reference/mappedTypeErrors.js b/tests/baselines/reference/mappedTypeErrors.js index f717f0d02f390..b2c8dc2e84a2f 100644 --- a/tests/baselines/reference/mappedTypeErrors.js +++ b/tests/baselines/reference/mappedTypeErrors.js @@ -42,7 +42,7 @@ type T12 = Pick; // Error type T13 = Pick; type T14 = Pick; // Error type T15 = Pick; -type T16 = Pick; +type T16 = Pick; // Error function f1(x: T) { let y: Pick; // Error From cd05c079ce0ebdf8daab9a568f24d0ca86729fc1 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 13 Nov 2016 15:25:16 -0800 Subject: [PATCH 28/28] Add comment explaining type alias instantiation strategy --- src/compiler/checker.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d75ae6a8cdc49..11b36ae80fb49 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6519,6 +6519,23 @@ namespace ts { function instantiateType(type: Type, mapper: TypeMapper): Type { if (type && mapper !== identityMapper) { + // If we are instantiating a type that has a top-level type alias, obtain the instantiation through + // the type alias instead in order to share instantiations for the same type arguments. This can + // dramatically reduce the number of structurally identical types we generate. Note that we can only + // perform this optimization for top-level type aliases. Consider: + // + // function f1(x: T) { + // type Foo = { x: X, t: T }; + // let obj: Foo = { x: x }; + // return obj; + // } + // function f2(x: U) { return f1(x); } + // let z = f2(42); + // + // Above, the declaration of f2 has an inferred return type that is an instantiation of f1's Foo + // equivalent to { x: U, t: U }. When instantiating this return type, we can't go back to Foo's + // cache because all cached instantiations are of the form { x: ???, t: T }, i.e. they have not been + // instantiated for T. Instead, we need to further instantiate the { x: U, t: U } form. if (type.aliasSymbol && isTopLevelTypeAlias(type.aliasSymbol)) { if (type.aliasTypeArguments) { return getTypeAliasInstantiation(type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));