diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fe5a746a74d..e8128fa4890 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3033,7 +3033,7 @@ namespace ts { return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode); } if (type.flags & TypeFlags.Substitution) { - return typeToTypeNodeHelper((type).typeParameter, context); + return typeToTypeNodeHelper((type).typeVariable, context); } Debug.fail("Should be unreachable."); @@ -7303,7 +7303,7 @@ namespace ts { const res = tryGetDeclaredTypeOfSymbol(symbol); if (res) { return checkNoTypeArguments(node, symbol) ? - res.flags & TypeFlags.TypeParameter ? getConstrainedTypeParameter(res, node) : res : + res.flags & TypeFlags.TypeParameter ? getConstrainedTypeVariable(res, node) : res : unknownType; } @@ -7342,25 +7342,25 @@ namespace ts { } } - function getSubstitutionType(typeParameter: TypeParameter, substitute: Type) { + function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) { const result = createType(TypeFlags.Substitution); - result.typeParameter = typeParameter; + result.typeVariable = typeVariable; result.substitute = substitute; return result; } - function getConstrainedTypeParameter(typeParameter: TypeParameter, node: Node) { + function getConstrainedTypeVariable(typeVariable: TypeVariable, node: Node) { let constraints: Type[]; while (isPartOfTypeNode(node)) { const parent = node.parent; if (parent.kind === SyntaxKind.ConditionalType && node === (parent).trueType) { - if (getTypeFromTypeNode((parent).checkType) === typeParameter) { + if (getActualTypeVariable(getTypeFromTypeNode((parent).checkType)) === typeVariable) { constraints = append(constraints, getTypeFromTypeNode((parent).extendsType)); } } node = parent; } - return constraints ? getSubstitutionType(typeParameter, getIntersectionType(append(constraints, typeParameter))) : typeParameter; + return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable; } function isJSDocTypeReference(node: TypeReferenceType): node is TypeReferenceNode { @@ -8256,7 +8256,13 @@ namespace ts { function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) { const links = getNodeLinks(node); if (!links.resolvedType) { - links.resolvedType = getIndexedAccessType(getTypeFromTypeNode(node.objectType), getTypeFromTypeNode(node.indexType), node); + const objectType = getTypeFromTypeNode(node.objectType); + const indexType = getTypeFromTypeNode(node.indexType); + const resolved = getIndexedAccessType(objectType, indexType, node); + links.resolvedType = resolved.flags & TypeFlags.IndexedAccess && + (resolved).objectType === objectType && + (resolved).indexType === indexType ? + getConstrainedTypeVariable(resolved, node) : resolved; } return links.resolvedType; } @@ -8276,8 +8282,8 @@ namespace ts { return links.resolvedType; } - function getActualTypeParameter(type: Type) { - return type.flags & TypeFlags.Substitution ? (type).typeParameter : type; + function getActualTypeVariable(type: Type) { + return type.flags & TypeFlags.Substitution ? (type).typeVariable : type; } function getConditionalType(root: ConditionalRoot, mapper: TypeMapper): Type { @@ -8321,7 +8327,7 @@ namespace ts { } } // Return a deferred type for a check that is neither definitely true nor definitely false - const erasedCheckType = getActualTypeParameter(checkType); + const erasedCheckType = getActualTypeVariable(checkType); const result = createType(TypeFlags.Conditional); result.root = root; result.checkType = erasedCheckType; @@ -9041,7 +9047,7 @@ namespace ts { return getConditionalTypeInstantiation(type, combineTypeMappers((type).mapper, mapper)); } if (type.flags & TypeFlags.Substitution) { - return mapper((type).typeParameter); + return instantiateType((type).typeVariable, mapper); } } return type; @@ -9647,10 +9653,10 @@ namespace ts { target = (target).regularType; } if (source.flags & TypeFlags.Substitution) { - source = relation === definitelyAssignableRelation ? (source).typeParameter : (source).substitute; + source = relation === definitelyAssignableRelation ? (source).typeVariable : (source).substitute; } if (target.flags & TypeFlags.Substitution) { - target = (target).typeParameter; + target = (target).typeVariable; } // both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7ed0f30916c..e63a9ae6efc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3844,6 +3844,8 @@ namespace ts { constraint?: Type; } + export type TypeVariable = TypeParameter | IndexedAccessType; + // keyof T types (TypeFlags.Index) export interface IndexType extends InstantiableType { type: InstantiableType | UnionOrIntersectionType; @@ -3875,14 +3877,14 @@ namespace ts { } // Type parameter substitution (TypeFlags.Substitution) - // Substitution types are created for type parameter references that occur in the true branch - // of a conditional type. For example, in 'T extends string ? Foo : Bar', the reference to - // T in Foo is resolved as a substitution type that substitutes 'string & T' for T. Thus, if - // Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution types - // disappear upon instantiation (just like type parameters). + // Substitution types are created for type parameters or indexed access types that occur in the + // true branch of a conditional type. For example, in 'T extends string ? Foo : Bar', the + // reference to T in Foo is resolved as a substitution type that substitutes 'string & T' for T. + // Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution + // types disappear upon instantiation (just like type parameters). export interface SubstitutionType extends InstantiableType { - typeParameter: TypeParameter; // Target type parameter - substitute: Type; // Type to substitute for type parameter + typeVariable: TypeVariable; // Target type variable + substitute: Type; // Type to substitute for type parameter } export const enum SignatureKind {