Skip to content

Commit 958487b

Browse files
authored
Mark indexed access object type comparisons as unreliable (#52106)
1 parent 211a60d commit 958487b

5 files changed

+231
-0
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21309,6 +21309,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2130921309
// Relate components directly before falling back to constraint relationships
2131021310
// A type S[K] is related to a type T[J] if S is related to T and K is related to J.
2131121311
if (result = isRelatedTo((source as IndexedAccessType).objectType, (target as IndexedAccessType).objectType, RecursionFlags.Both, reportErrors)) {
21312+
// This does _not_ generalize - specific instantiations of `S[K]` and `T[J]` may be related, even if the indexed accesses generally are not.
21313+
// For example, `S = {x: string, a: string}`, `T = {x: string, b: string}`, `K = J = "x"`. `S` and `T` are unrelated, but the result of executing
21314+
// `S["x"]` and `T["x"]` _are_. Given that, we have to flag the object type comparison here as "unreliable", since while the generic result can reliably
21315+
// be used in the affirmative case, it failing is not an indicator that the structural result will not succeed.
21316+
instantiateType((source as IndexedAccessType).objectType, reportUnreliableMapper);
2131221317
result &= isRelatedTo((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType, RecursionFlags.Both, reportErrors);
2131321318
}
2131421319
if (result) {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//// [genericIndexedAccessVarianceComparisonResultCorrect.ts]
2+
class A {
3+
x: string = 'A';
4+
y: number = 0;
5+
}
6+
7+
class B {
8+
x: string = 'B';
9+
z: boolean = true;
10+
}
11+
12+
type T<X extends { x: any }> = Pick<X, 'x'>;
13+
14+
type C = T<A>;
15+
type D = T<B>;
16+
17+
type C_extends_D = C extends D ? true : false; // true
18+
type PickA_extends_PickB = Pick<A, 'x'> extends Pick<B, 'x'> ? true : false; // true
19+
type TA_extends_TB = T<A> extends T<B> ? true : false; // should be true
20+
21+
declare let a: T<A>;
22+
declare let b: T<B>;
23+
declare let c: C;
24+
declare let d: D;
25+
26+
b = a; // should be no error
27+
c = d;
28+
29+
//// [genericIndexedAccessVarianceComparisonResultCorrect.js]
30+
var A = /** @class */ (function () {
31+
function A() {
32+
this.x = 'A';
33+
this.y = 0;
34+
}
35+
return A;
36+
}());
37+
var B = /** @class */ (function () {
38+
function B() {
39+
this.x = 'B';
40+
this.z = true;
41+
}
42+
return B;
43+
}());
44+
b = a; // should be no error
45+
c = d;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
=== tests/cases/compiler/genericIndexedAccessVarianceComparisonResultCorrect.ts ===
2+
class A {
3+
>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0))
4+
5+
x: string = 'A';
6+
>x : Symbol(A.x, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 9))
7+
8+
y: number = 0;
9+
>y : Symbol(A.y, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 1, 20))
10+
}
11+
12+
class B {
13+
>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1))
14+
15+
x: string = 'B';
16+
>x : Symbol(B.x, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 5, 9))
17+
18+
z: boolean = true;
19+
>z : Symbol(B.z, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 6, 20))
20+
}
21+
22+
type T<X extends { x: any }> = Pick<X, 'x'>;
23+
>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1))
24+
>X : Symbol(X, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 7))
25+
>x : Symbol(x, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 18))
26+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
27+
>X : Symbol(X, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 7))
28+
29+
type C = T<A>;
30+
>C : Symbol(C, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 44))
31+
>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1))
32+
>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0))
33+
34+
type D = T<B>;
35+
>D : Symbol(D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 12, 14))
36+
>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1))
37+
>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1))
38+
39+
type C_extends_D = C extends D ? true : false; // true
40+
>C_extends_D : Symbol(C_extends_D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 13, 14))
41+
>C : Symbol(C, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 44))
42+
>D : Symbol(D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 12, 14))
43+
44+
type PickA_extends_PickB = Pick<A, 'x'> extends Pick<B, 'x'> ? true : false; // true
45+
>PickA_extends_PickB : Symbol(PickA_extends_PickB, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 15, 46))
46+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
47+
>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0))
48+
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
49+
>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1))
50+
51+
type TA_extends_TB = T<A> extends T<B> ? true : false; // should be true
52+
>TA_extends_TB : Symbol(TA_extends_TB, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 16, 76))
53+
>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1))
54+
>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0))
55+
>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1))
56+
>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1))
57+
58+
declare let a: T<A>;
59+
>a : Symbol(a, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 19, 11))
60+
>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1))
61+
>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0))
62+
63+
declare let b: T<B>;
64+
>b : Symbol(b, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 20, 11))
65+
>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1))
66+
>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1))
67+
68+
declare let c: C;
69+
>c : Symbol(c, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 21, 11))
70+
>C : Symbol(C, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 44))
71+
72+
declare let d: D;
73+
>d : Symbol(d, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 22, 11))
74+
>D : Symbol(D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 12, 14))
75+
76+
b = a; // should be no error
77+
>b : Symbol(b, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 20, 11))
78+
>a : Symbol(a, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 19, 11))
79+
80+
c = d;
81+
>c : Symbol(c, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 21, 11))
82+
>d : Symbol(d, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 22, 11))
83+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
=== tests/cases/compiler/genericIndexedAccessVarianceComparisonResultCorrect.ts ===
2+
class A {
3+
>A : A
4+
5+
x: string = 'A';
6+
>x : string
7+
>'A' : "A"
8+
9+
y: number = 0;
10+
>y : number
11+
>0 : 0
12+
}
13+
14+
class B {
15+
>B : B
16+
17+
x: string = 'B';
18+
>x : string
19+
>'B' : "B"
20+
21+
z: boolean = true;
22+
>z : boolean
23+
>true : true
24+
}
25+
26+
type T<X extends { x: any }> = Pick<X, 'x'>;
27+
>T : T<X>
28+
>x : any
29+
30+
type C = T<A>;
31+
>C : { x: string; }
32+
33+
type D = T<B>;
34+
>D : { x: string; }
35+
36+
type C_extends_D = C extends D ? true : false; // true
37+
>C_extends_D : true
38+
>true : true
39+
>false : false
40+
41+
type PickA_extends_PickB = Pick<A, 'x'> extends Pick<B, 'x'> ? true : false; // true
42+
>PickA_extends_PickB : true
43+
>true : true
44+
>false : false
45+
46+
type TA_extends_TB = T<A> extends T<B> ? true : false; // should be true
47+
>TA_extends_TB : true
48+
>true : true
49+
>false : false
50+
51+
declare let a: T<A>;
52+
>a : T<A>
53+
54+
declare let b: T<B>;
55+
>b : T<B>
56+
57+
declare let c: C;
58+
>c : C
59+
60+
declare let d: D;
61+
>d : D
62+
63+
b = a; // should be no error
64+
>b = a : T<A>
65+
>b : T<B>
66+
>a : T<A>
67+
68+
c = d;
69+
>c = d : D
70+
>c : C
71+
>d : D
72+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class A {
2+
x: string = 'A';
3+
y: number = 0;
4+
}
5+
6+
class B {
7+
x: string = 'B';
8+
z: boolean = true;
9+
}
10+
11+
type T<X extends { x: any }> = Pick<X, 'x'>;
12+
13+
type C = T<A>;
14+
type D = T<B>;
15+
16+
type C_extends_D = C extends D ? true : false; // true
17+
type PickA_extends_PickB = Pick<A, 'x'> extends Pick<B, 'x'> ? true : false; // true
18+
type TA_extends_TB = T<A> extends T<B> ? true : false; // should be true
19+
20+
declare let a: T<A>;
21+
declare let b: T<B>;
22+
declare let c: C;
23+
declare let d: D;
24+
25+
b = a; // should be no error
26+
c = d;

0 commit comments

Comments
 (0)