Skip to content

Commit 9509a54

Browse files
authored
Merge pull request #31141 from Microsoft/fixInferenceToIndexedAccessWithSubstitution
Fix inference to indexed access type containing substitution type
2 parents d1646c7 + 1818218 commit 9509a54

5 files changed

+167
-5
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10349,8 +10349,16 @@ namespace ts {
1034910349
return links.resolvedType;
1035010350
}
1035110351

10352-
function getActualTypeVariable(type: Type) {
10353-
return type.flags & TypeFlags.Substitution ? (<SubstitutionType>type).typeVariable : type;
10352+
function getActualTypeVariable(type: Type): Type {
10353+
if (type.flags & TypeFlags.Substitution) {
10354+
return (<SubstitutionType>type).typeVariable;
10355+
}
10356+
if (type.flags & TypeFlags.IndexedAccess && (
10357+
(<IndexedAccessType>type).objectType.flags & TypeFlags.Substitution ||
10358+
(<IndexedAccessType>type).indexType.flags & TypeFlags.Substitution)) {
10359+
return getIndexedAccessType(getActualTypeVariable((<IndexedAccessType>type).objectType), getActualTypeVariable((<IndexedAccessType>type).indexType));
10360+
}
10361+
return type;
1035410362
}
1035510363

1035610364
/**
@@ -15030,6 +15038,9 @@ namespace ts {
1503015038
target = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
1503115039
}
1503215040
}
15041+
else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
15042+
target = getActualTypeVariable(target);
15043+
}
1503315044
if (target.flags & TypeFlags.TypeVariable) {
1503415045
// If target is a type parameter, make an inference, unless the source type contains
1503515046
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
@@ -15091,9 +15102,6 @@ namespace ts {
1509115102
}
1509215103
}
1509315104
}
15094-
else if (target.flags & TypeFlags.Substitution) {
15095-
inferFromTypes(source, (target as SubstitutionType).typeVariable);
15096-
}
1509715105
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
1509815106
// If source and target are references to the same generic type, infer from type arguments
1509915107
const sourceTypes = (<TypeReference>source).typeArguments || emptyArray;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [substitutionTypesInIndexedAccessTypes.ts]
2+
// Repro from #31086
3+
4+
type UserArgs = {
5+
select?: boolean
6+
};
7+
8+
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
9+
10+
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
11+
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
12+
13+
const boundaryResult = withBoundary({
14+
select: true,
15+
});
16+
17+
const withoutBoundaryResult = withoutBoundary({
18+
select: true,
19+
});
20+
21+
22+
//// [substitutionTypesInIndexedAccessTypes.js]
23+
"use strict";
24+
// Repro from #31086
25+
var boundaryResult = withBoundary({
26+
select: true
27+
});
28+
var withoutBoundaryResult = withoutBoundary({
29+
select: true
30+
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
=== tests/cases/compiler/substitutionTypesInIndexedAccessTypes.ts ===
2+
// Repro from #31086
3+
4+
type UserArgs = {
5+
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
6+
7+
select?: boolean
8+
>select : Symbol(select, Decl(substitutionTypesInIndexedAccessTypes.ts, 2, 17))
9+
10+
};
11+
12+
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
13+
>Subset : Symbol(Subset, Decl(substitutionTypesInIndexedAccessTypes.ts, 4, 2))
14+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 12))
15+
>U : Symbol(U, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 14))
16+
>key : Symbol(key, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 23))
17+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 12))
18+
>key : Symbol(key, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 23))
19+
>U : Symbol(U, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 14))
20+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 12))
21+
>key : Symbol(key, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 23))
22+
23+
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
24+
>withBoundary : Symbol(withBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 79))
25+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 30))
26+
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
27+
>args : Symbol(args, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 50))
28+
>Subset : Symbol(Subset, Decl(substitutionTypesInIndexedAccessTypes.ts, 4, 2))
29+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 30))
30+
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
31+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 30))
32+
33+
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
34+
>withoutBoundary : Symbol(withoutBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 81))
35+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 33))
36+
>UserArgs : Symbol(UserArgs, Decl(substitutionTypesInIndexedAccessTypes.ts, 0, 0))
37+
>args : Symbol(args, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 53))
38+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 33))
39+
>T : Symbol(T, Decl(substitutionTypesInIndexedAccessTypes.ts, 9, 33))
40+
41+
const boundaryResult = withBoundary({
42+
>boundaryResult : Symbol(boundaryResult, Decl(substitutionTypesInIndexedAccessTypes.ts, 11, 5))
43+
>withBoundary : Symbol(withBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 6, 79))
44+
45+
select: true,
46+
>select : Symbol(select, Decl(substitutionTypesInIndexedAccessTypes.ts, 11, 37))
47+
48+
});
49+
50+
const withoutBoundaryResult = withoutBoundary({
51+
>withoutBoundaryResult : Symbol(withoutBoundaryResult, Decl(substitutionTypesInIndexedAccessTypes.ts, 15, 5))
52+
>withoutBoundary : Symbol(withoutBoundary, Decl(substitutionTypesInIndexedAccessTypes.ts, 8, 81))
53+
54+
select: true,
55+
>select : Symbol(select, Decl(substitutionTypesInIndexedAccessTypes.ts, 15, 47))
56+
57+
});
58+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
=== tests/cases/compiler/substitutionTypesInIndexedAccessTypes.ts ===
2+
// Repro from #31086
3+
4+
type UserArgs = {
5+
>UserArgs : UserArgs
6+
7+
select?: boolean
8+
>select : boolean | undefined
9+
10+
};
11+
12+
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
13+
>Subset : Subset<T, U>
14+
15+
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
16+
>withBoundary : <T extends UserArgs>(args?: Subset<T, UserArgs> | undefined) => T
17+
>args : Subset<T, UserArgs> | undefined
18+
19+
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
20+
>withoutBoundary : <T extends UserArgs>(args?: T | undefined) => T
21+
>args : T | undefined
22+
23+
const boundaryResult = withBoundary({
24+
>boundaryResult : { select: true; }
25+
>withBoundary({ select: true,}) : { select: true; }
26+
>withBoundary : <T extends UserArgs>(args?: Subset<T, UserArgs> | undefined) => T
27+
>{ select: true,} : { select: true; }
28+
29+
select: true,
30+
>select : true
31+
>true : true
32+
33+
});
34+
35+
const withoutBoundaryResult = withoutBoundary({
36+
>withoutBoundaryResult : { select: true; }
37+
>withoutBoundary({ select: true,}) : { select: true; }
38+
>withoutBoundary : <T extends UserArgs>(args?: T | undefined) => T
39+
>{ select: true,} : { select: true; }
40+
41+
select: true,
42+
>select : true
43+
>true : true
44+
45+
});
46+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @strict: true
2+
3+
// Repro from #31086
4+
5+
type UserArgs = {
6+
select?: boolean
7+
};
8+
9+
type Subset<T, U> = { [key in keyof T]: key extends keyof U ? T[key] : never };
10+
11+
declare function withBoundary<T extends UserArgs>(args?: Subset<T, UserArgs>): T;
12+
declare function withoutBoundary<T extends UserArgs>(args?: T): T;
13+
14+
const boundaryResult = withBoundary({
15+
select: true,
16+
});
17+
18+
const withoutBoundaryResult = withoutBoundary({
19+
select: true,
20+
});

0 commit comments

Comments
 (0)