Skip to content

Commit d90814b

Browse files
authored
Merge pull request #18391 from Microsoft/fixMappedTypeInference
Inference for higher order mapped, index and lookup types
2 parents d65a3e1 + 0823eba commit d90814b

5 files changed

+284
-1
lines changed

src/compiler/checker.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -10387,7 +10387,7 @@ namespace ts {
1038710387
// results for union and intersection types for performance reasons.
1038810388
function couldContainTypeVariables(type: Type): boolean {
1038910389
const objectFlags = getObjectFlags(type);
10390-
return !!(type.flags & TypeFlags.TypeVariable ||
10390+
return !!(type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) ||
1039110391
objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) ||
1039210392
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
1039310393
objectFlags & ObjectFlags.Mapped ||
@@ -10555,6 +10555,13 @@ namespace ts {
1055510555
inferFromTypes(sourceTypes[i], targetTypes[i]);
1055610556
}
1055710557
}
10558+
else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) {
10559+
inferFromTypes((<IndexType>source).type, (<IndexType>target).type);
10560+
}
10561+
else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
10562+
inferFromTypes((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType);
10563+
inferFromTypes((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType);
10564+
}
1055810565
else if (target.flags & TypeFlags.UnionOrIntersection) {
1055910566
const targetTypes = (<UnionOrIntersectionType>target).types;
1056010567
let typeVariableCount = 0;
@@ -10628,6 +10635,12 @@ namespace ts {
1062810635
}
1062910636

1063010637
function inferFromObjectTypes(source: Type, target: Type) {
10638+
if (isGenericMappedType(source) && isGenericMappedType(target)) {
10639+
// The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer
10640+
// from S to T and from X to Y.
10641+
inferFromTypes(getConstraintTypeFromMappedType(<MappedType>source), getConstraintTypeFromMappedType(<MappedType>target));
10642+
inferFromTypes(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target));
10643+
}
1063110644
if (getObjectFlags(target) & ObjectFlags.Mapped) {
1063210645
const constraintType = getConstraintTypeFromMappedType(<MappedType>target);
1063310646
if (constraintType.flags & TypeFlags.Index) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [higherOrderMappedIndexLookupInference.ts]
2+
// @strict
3+
4+
function f1(a: <T>() => keyof T, b: <U>() => keyof U) {
5+
a = b;
6+
b = a;
7+
}
8+
9+
function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) {
10+
a = b;
11+
b = a;
12+
}
13+
14+
function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) {
15+
a = b;
16+
b = a;
17+
}
18+
19+
// Repro from #18338
20+
21+
type IdMapped<T> = { [K in keyof T]: T[K] }
22+
23+
declare const f: <T>() => IdMapped<T>;
24+
declare const g: <U>() => { [K in keyof U]: U[K] };
25+
26+
const h: typeof g = f;
27+
28+
29+
//// [higherOrderMappedIndexLookupInference.js]
30+
// @strict
31+
function f1(a, b) {
32+
a = b;
33+
b = a;
34+
}
35+
function f2(a, b) {
36+
a = b;
37+
b = a;
38+
}
39+
function f3(a, b) {
40+
a = b;
41+
b = a;
42+
}
43+
var h = f;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
=== tests/cases/compiler/higherOrderMappedIndexLookupInference.ts ===
2+
// @strict
3+
4+
function f1(a: <T>() => keyof T, b: <U>() => keyof U) {
5+
>f1 : Symbol(f1, Decl(higherOrderMappedIndexLookupInference.ts, 0, 0))
6+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12))
7+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 2, 16))
8+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 2, 16))
9+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32))
10+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 2, 37))
11+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 2, 37))
12+
13+
a = b;
14+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12))
15+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32))
16+
17+
b = a;
18+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32))
19+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12))
20+
}
21+
22+
function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) {
23+
>f2 : Symbol(f2, Decl(higherOrderMappedIndexLookupInference.ts, 5, 1))
24+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12))
25+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16))
26+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 7, 18))
27+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16))
28+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16))
29+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 7, 18))
30+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48))
31+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53))
32+
>L : Symbol(L, Decl(higherOrderMappedIndexLookupInference.ts, 7, 55))
33+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53))
34+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53))
35+
>L : Symbol(L, Decl(higherOrderMappedIndexLookupInference.ts, 7, 55))
36+
37+
a = b;
38+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12))
39+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48))
40+
41+
b = a;
42+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48))
43+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12))
44+
}
45+
46+
function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) {
47+
>f3 : Symbol(f3, Decl(higherOrderMappedIndexLookupInference.ts, 10, 1))
48+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12))
49+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16))
50+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 27))
51+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16))
52+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16))
53+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 27))
54+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49))
55+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54))
56+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 65))
57+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54))
58+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54))
59+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 65))
60+
61+
a = b;
62+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12))
63+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49))
64+
65+
b = a;
66+
>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49))
67+
>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12))
68+
}
69+
70+
// Repro from #18338
71+
72+
type IdMapped<T> = { [K in keyof T]: T[K] }
73+
>IdMapped : Symbol(IdMapped, Decl(higherOrderMappedIndexLookupInference.ts, 15, 1))
74+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14))
75+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 19, 22))
76+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14))
77+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14))
78+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 19, 22))
79+
80+
declare const f: <T>() => IdMapped<T>;
81+
>f : Symbol(f, Decl(higherOrderMappedIndexLookupInference.ts, 21, 13))
82+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 21, 18))
83+
>IdMapped : Symbol(IdMapped, Decl(higherOrderMappedIndexLookupInference.ts, 15, 1))
84+
>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 21, 18))
85+
86+
declare const g: <U>() => { [K in keyof U]: U[K] };
87+
>g : Symbol(g, Decl(higherOrderMappedIndexLookupInference.ts, 22, 13))
88+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18))
89+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 22, 29))
90+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18))
91+
>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18))
92+
>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 22, 29))
93+
94+
const h: typeof g = f;
95+
>h : Symbol(h, Decl(higherOrderMappedIndexLookupInference.ts, 24, 5))
96+
>g : Symbol(g, Decl(higherOrderMappedIndexLookupInference.ts, 22, 13))
97+
>f : Symbol(f, Decl(higherOrderMappedIndexLookupInference.ts, 21, 13))
98+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
=== tests/cases/compiler/higherOrderMappedIndexLookupInference.ts ===
2+
// @strict
3+
4+
function f1(a: <T>() => keyof T, b: <U>() => keyof U) {
5+
>f1 : (a: <T>() => keyof T, b: <U>() => keyof U) => void
6+
>a : <T>() => keyof T
7+
>T : T
8+
>T : T
9+
>b : <U>() => keyof U
10+
>U : U
11+
>U : U
12+
13+
a = b;
14+
>a = b : <U>() => keyof U
15+
>a : <T>() => keyof T
16+
>b : <U>() => keyof U
17+
18+
b = a;
19+
>b = a : <T>() => keyof T
20+
>b : <U>() => keyof U
21+
>a : <T>() => keyof T
22+
}
23+
24+
function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) {
25+
>f2 : (a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) => void
26+
>a : <T, K extends keyof T>() => T[K]
27+
>T : T
28+
>K : K
29+
>T : T
30+
>T : T
31+
>K : K
32+
>b : <U, L extends keyof U>() => U[L]
33+
>U : U
34+
>L : L
35+
>U : U
36+
>U : U
37+
>L : L
38+
39+
a = b;
40+
>a = b : <U, L extends keyof U>() => U[L]
41+
>a : <T, K extends keyof T>() => T[K]
42+
>b : <U, L extends keyof U>() => U[L]
43+
44+
b = a;
45+
>b = a : <T, K extends keyof T>() => T[K]
46+
>b : <U, L extends keyof U>() => U[L]
47+
>a : <T, K extends keyof T>() => T[K]
48+
}
49+
50+
function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) {
51+
>f3 : (a: <T>() => { [K in keyof T]: T[K]; }, b: <U>() => { [K in keyof U]: U[K]; }) => void
52+
>a : <T>() => { [K in keyof T]: T[K]; }
53+
>T : T
54+
>K : K
55+
>T : T
56+
>T : T
57+
>K : K
58+
>b : <U>() => { [K in keyof U]: U[K]; }
59+
>U : U
60+
>K : K
61+
>U : U
62+
>U : U
63+
>K : K
64+
65+
a = b;
66+
>a = b : <U>() => { [K in keyof U]: U[K]; }
67+
>a : <T>() => { [K in keyof T]: T[K]; }
68+
>b : <U>() => { [K in keyof U]: U[K]; }
69+
70+
b = a;
71+
>b = a : <T>() => { [K in keyof T]: T[K]; }
72+
>b : <U>() => { [K in keyof U]: U[K]; }
73+
>a : <T>() => { [K in keyof T]: T[K]; }
74+
}
75+
76+
// Repro from #18338
77+
78+
type IdMapped<T> = { [K in keyof T]: T[K] }
79+
>IdMapped : IdMapped<T>
80+
>T : T
81+
>K : K
82+
>T : T
83+
>T : T
84+
>K : K
85+
86+
declare const f: <T>() => IdMapped<T>;
87+
>f : <T>() => IdMapped<T>
88+
>T : T
89+
>IdMapped : IdMapped<T>
90+
>T : T
91+
92+
declare const g: <U>() => { [K in keyof U]: U[K] };
93+
>g : <U>() => { [K in keyof U]: U[K]; }
94+
>U : U
95+
>K : K
96+
>U : U
97+
>U : U
98+
>K : K
99+
100+
const h: typeof g = f;
101+
>h : <U>() => { [K in keyof U]: U[K]; }
102+
>g : <U>() => { [K in keyof U]: U[K]; }
103+
>f : <T>() => IdMapped<T>
104+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @strict
2+
3+
function f1(a: <T>() => keyof T, b: <U>() => keyof U) {
4+
a = b;
5+
b = a;
6+
}
7+
8+
function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) {
9+
a = b;
10+
b = a;
11+
}
12+
13+
function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) {
14+
a = b;
15+
b = a;
16+
}
17+
18+
// Repro from #18338
19+
20+
type IdMapped<T> = { [K in keyof T]: T[K] }
21+
22+
declare const f: <T>() => IdMapped<T>;
23+
declare const g: <U>() => { [K in keyof U]: U[K] };
24+
25+
const h: typeof g = f;

0 commit comments

Comments
 (0)