From 2170ff6f16005ff03deb0f19806095c6c5a68c8d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 10 Nov 2016 09:11:21 -0800 Subject: [PATCH] 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 cfabcd3f5ba..14e01cbc3a2 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 99d776650b4..13b313f1689 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 }