Skip to content

Commit de1ebb4

Browse files
committed
Add constraints to indexed access types in conditional types
1 parent 76fefdd commit de1ebb4

File tree

2 files changed

+29
-21
lines changed

2 files changed

+29
-21
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3033,7 +3033,7 @@ namespace ts {
30333033
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
30343034
}
30353035
if (type.flags & TypeFlags.Substitution) {
3036-
return typeToTypeNodeHelper((<SubstitutionType>type).typeParameter, context);
3036+
return typeToTypeNodeHelper((<SubstitutionType>type).typeVariable, context);
30373037
}
30383038

30393039
Debug.fail("Should be unreachable.");
@@ -7303,7 +7303,7 @@ namespace ts {
73037303
const res = tryGetDeclaredTypeOfSymbol(symbol);
73047304
if (res) {
73057305
return checkNoTypeArguments(node, symbol) ?
7306-
res.flags & TypeFlags.TypeParameter ? getConstrainedTypeParameter(<TypeParameter>res, node) : res :
7306+
res.flags & TypeFlags.TypeParameter ? getConstrainedTypeVariable(<TypeParameter>res, node) : res :
73077307
unknownType;
73087308
}
73097309

@@ -7342,25 +7342,25 @@ namespace ts {
73427342
}
73437343
}
73447344

7345-
function getSubstitutionType(typeParameter: TypeParameter, substitute: Type) {
7345+
function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) {
73467346
const result = <SubstitutionType>createType(TypeFlags.Substitution);
7347-
result.typeParameter = typeParameter;
7347+
result.typeVariable = typeVariable;
73487348
result.substitute = substitute;
73497349
return result;
73507350
}
73517351

7352-
function getConstrainedTypeParameter(typeParameter: TypeParameter, node: Node) {
7352+
function getConstrainedTypeVariable(typeVariable: TypeVariable, node: Node) {
73537353
let constraints: Type[];
73547354
while (isPartOfTypeNode(node)) {
73557355
const parent = node.parent;
73567356
if (parent.kind === SyntaxKind.ConditionalType && node === (<ConditionalTypeNode>parent).trueType) {
7357-
if (getTypeFromTypeNode((<ConditionalTypeNode>parent).checkType) === typeParameter) {
7357+
if (getActualTypeVariable(getTypeFromTypeNode((<ConditionalTypeNode>parent).checkType)) === typeVariable) {
73587358
constraints = append(constraints, getTypeFromTypeNode((<ConditionalTypeNode>parent).extendsType));
73597359
}
73607360
}
73617361
node = parent;
73627362
}
7363-
return constraints ? getSubstitutionType(typeParameter, getIntersectionType(append(constraints, typeParameter))) : typeParameter;
7363+
return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable;
73647364
}
73657365

73667366
function isJSDocTypeReference(node: TypeReferenceType): node is TypeReferenceNode {
@@ -8256,7 +8256,13 @@ namespace ts {
82568256
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
82578257
const links = getNodeLinks(node);
82588258
if (!links.resolvedType) {
8259-
links.resolvedType = getIndexedAccessType(getTypeFromTypeNode(node.objectType), getTypeFromTypeNode(node.indexType), node);
8259+
const objectType = getTypeFromTypeNode(node.objectType);
8260+
const indexType = getTypeFromTypeNode(node.indexType);
8261+
const resolved = getIndexedAccessType(objectType, indexType, node);
8262+
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
8263+
(<IndexedAccessType>resolved).objectType === objectType &&
8264+
(<IndexedAccessType>resolved).indexType === indexType ?
8265+
getConstrainedTypeVariable(<IndexedAccessType>resolved, node) : resolved;
82608266
}
82618267
return links.resolvedType;
82628268
}
@@ -8276,8 +8282,8 @@ namespace ts {
82768282
return links.resolvedType;
82778283
}
82788284

8279-
function getActualTypeParameter(type: Type) {
8280-
return type.flags & TypeFlags.Substitution ? (<SubstitutionType>type).typeParameter : type;
8285+
function getActualTypeVariable(type: Type) {
8286+
return type.flags & TypeFlags.Substitution ? (<SubstitutionType>type).typeVariable : type;
82818287
}
82828288

82838289
function getConditionalType(root: ConditionalRoot, mapper: TypeMapper): Type {
@@ -8321,7 +8327,7 @@ namespace ts {
83218327
}
83228328
}
83238329
// Return a deferred type for a check that is neither definitely true nor definitely false
8324-
const erasedCheckType = getActualTypeParameter(checkType);
8330+
const erasedCheckType = getActualTypeVariable(checkType);
83258331
const result = <ConditionalType>createType(TypeFlags.Conditional);
83268332
result.root = root;
83278333
result.checkType = erasedCheckType;
@@ -9041,7 +9047,7 @@ namespace ts {
90419047
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
90429048
}
90439049
if (type.flags & TypeFlags.Substitution) {
9044-
return mapper((<SubstitutionType>type).typeParameter);
9050+
return instantiateType((<SubstitutionType>type).typeVariable, mapper);
90459051
}
90469052
}
90479053
return type;
@@ -9647,10 +9653,10 @@ namespace ts {
96479653
target = (<LiteralType>target).regularType;
96489654
}
96499655
if (source.flags & TypeFlags.Substitution) {
9650-
source = relation === definitelyAssignableRelation ? (<SubstitutionType>source).typeParameter : (<SubstitutionType>source).substitute;
9656+
source = relation === definitelyAssignableRelation ? (<SubstitutionType>source).typeVariable : (<SubstitutionType>source).substitute;
96519657
}
96529658
if (target.flags & TypeFlags.Substitution) {
9653-
target = (<SubstitutionType>target).typeParameter;
9659+
target = (<SubstitutionType>target).typeVariable;
96549660
}
96559661

96569662
// both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases

src/compiler/types.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3844,6 +3844,8 @@ namespace ts {
38443844
constraint?: Type;
38453845
}
38463846

3847+
export type TypeVariable = TypeParameter | IndexedAccessType;
3848+
38473849
// keyof T types (TypeFlags.Index)
38483850
export interface IndexType extends InstantiableType {
38493851
type: InstantiableType | UnionOrIntersectionType;
@@ -3875,14 +3877,14 @@ namespace ts {
38753877
}
38763878

38773879
// Type parameter substitution (TypeFlags.Substitution)
3878-
// Substitution types are created for type parameter references that occur in the true branch
3879-
// of a conditional type. For example, in 'T extends string ? Foo<T> : Bar<T>', the reference to
3880-
// T in Foo<T> is resolved as a substitution type that substitutes 'string & T' for T. Thus, if
3881-
// Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution types
3882-
// disappear upon instantiation (just like type parameters).
3880+
// Substitution types are created for type parameters or indexed access types that occur in the
3881+
// true branch of a conditional type. For example, in 'T extends string ? Foo<T> : Bar<T>', the
3882+
// reference to T in Foo<T> is resolved as a substitution type that substitutes 'string & T' for T.
3883+
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution
3884+
// types disappear upon instantiation (just like type parameters).
38833885
export interface SubstitutionType extends InstantiableType {
3884-
typeParameter: TypeParameter; // Target type parameter
3885-
substitute: Type; // Type to substitute for type parameter
3886+
typeVariable: TypeVariable; // Target type variable
3887+
substitute: Type; // Type to substitute for type parameter
38863888
}
38873889

38883890
export const enum SignatureKind {

0 commit comments

Comments
 (0)