diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4c1ad31d43ff6..31cf2e1acebf1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20893,6 +20893,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Relate components directly before falling back to constraint relationships // A type S[K] is related to a type T[J] if S is related to T and K is related to J. if (result = isRelatedTo((source as IndexedAccessType).objectType, (target as IndexedAccessType).objectType, RecursionFlags.Both, reportErrors)) { + // This does _not_ generalize - specific instantiations of `S[K]` and `T[J]` may be related, even if the indexed accesses generally are not. + // 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 + // `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 + // be used in the affirmative case, it failing is not an indicator that the structural result will not succeed. + instantiateType((source as IndexedAccessType).objectType, reportUnreliableMapper); result &= isRelatedTo((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType, RecursionFlags.Both, reportErrors); } if (result) { diff --git a/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.js b/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.js new file mode 100644 index 0000000000000..f8daca1c7f6ca --- /dev/null +++ b/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.js @@ -0,0 +1,45 @@ +//// [genericIndexedAccessVarianceComparisonResultCorrect.ts] +class A { + x: string = 'A'; + y: number = 0; +} + +class B { + x: string = 'B'; + z: boolean = true; +} + +type T = Pick; + +type C = T; +type D = T; + +type C_extends_D = C extends D ? true : false; // true +type PickA_extends_PickB = Pick extends Pick ? true : false; // true +type TA_extends_TB = T extends T ? true : false; // should be true + +declare let a: T; +declare let b: T; +declare let c: C; +declare let d: D; + +b = a; // should be no error +c = d; + +//// [genericIndexedAccessVarianceComparisonResultCorrect.js] +var A = /** @class */ (function () { + function A() { + this.x = 'A'; + this.y = 0; + } + return A; +}()); +var B = /** @class */ (function () { + function B() { + this.x = 'B'; + this.z = true; + } + return B; +}()); +b = a; // should be no error +c = d; diff --git a/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.symbols b/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.symbols new file mode 100644 index 0000000000000..5636f82258090 --- /dev/null +++ b/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.symbols @@ -0,0 +1,83 @@ +=== tests/cases/compiler/genericIndexedAccessVarianceComparisonResultCorrect.ts === +class A { +>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0)) + + x: string = 'A'; +>x : Symbol(A.x, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 9)) + + y: number = 0; +>y : Symbol(A.y, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 1, 20)) +} + +class B { +>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1)) + + x: string = 'B'; +>x : Symbol(B.x, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 5, 9)) + + z: boolean = true; +>z : Symbol(B.z, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 6, 20)) +} + +type T = Pick; +>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1)) +>X : Symbol(X, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 7)) +>x : Symbol(x, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 18)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>X : Symbol(X, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 7)) + +type C = T; +>C : Symbol(C, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 44)) +>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1)) +>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0)) + +type D = T; +>D : Symbol(D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 12, 14)) +>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1)) +>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1)) + +type C_extends_D = C extends D ? true : false; // true +>C_extends_D : Symbol(C_extends_D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 13, 14)) +>C : Symbol(C, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 44)) +>D : Symbol(D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 12, 14)) + +type PickA_extends_PickB = Pick extends Pick ? true : false; // true +>PickA_extends_PickB : Symbol(PickA_extends_PickB, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 15, 46)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1)) + +type TA_extends_TB = T extends T ? true : false; // should be true +>TA_extends_TB : Symbol(TA_extends_TB, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 16, 76)) +>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1)) +>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0)) +>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1)) +>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1)) + +declare let a: T; +>a : Symbol(a, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 19, 11)) +>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1)) +>A : Symbol(A, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 0, 0)) + +declare let b: T; +>b : Symbol(b, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 20, 11)) +>T : Symbol(T, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 8, 1)) +>B : Symbol(B, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 3, 1)) + +declare let c: C; +>c : Symbol(c, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 21, 11)) +>C : Symbol(C, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 10, 44)) + +declare let d: D; +>d : Symbol(d, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 22, 11)) +>D : Symbol(D, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 12, 14)) + +b = a; // should be no error +>b : Symbol(b, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 20, 11)) +>a : Symbol(a, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 19, 11)) + +c = d; +>c : Symbol(c, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 21, 11)) +>d : Symbol(d, Decl(genericIndexedAccessVarianceComparisonResultCorrect.ts, 22, 11)) + diff --git a/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.types b/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.types new file mode 100644 index 0000000000000..e04f48ba89210 --- /dev/null +++ b/tests/baselines/reference/genericIndexedAccessVarianceComparisonResultCorrect.types @@ -0,0 +1,72 @@ +=== tests/cases/compiler/genericIndexedAccessVarianceComparisonResultCorrect.ts === +class A { +>A : A + + x: string = 'A'; +>x : string +>'A' : "A" + + y: number = 0; +>y : number +>0 : 0 +} + +class B { +>B : B + + x: string = 'B'; +>x : string +>'B' : "B" + + z: boolean = true; +>z : boolean +>true : true +} + +type T = Pick; +>T : T +>x : any + +type C = T; +>C : { x: string; } + +type D = T; +>D : { x: string; } + +type C_extends_D = C extends D ? true : false; // true +>C_extends_D : true +>true : true +>false : false + +type PickA_extends_PickB = Pick extends Pick ? true : false; // true +>PickA_extends_PickB : true +>true : true +>false : false + +type TA_extends_TB = T extends T ? true : false; // should be true +>TA_extends_TB : true +>true : true +>false : false + +declare let a: T; +>a : T + +declare let b: T; +>b : T + +declare let c: C; +>c : C + +declare let d: D; +>d : D + +b = a; // should be no error +>b = a : T +>b : T +>a : T + +c = d; +>c = d : D +>c : C +>d : D + diff --git a/tests/cases/compiler/genericIndexedAccessVarianceComparisonResultCorrect.ts b/tests/cases/compiler/genericIndexedAccessVarianceComparisonResultCorrect.ts new file mode 100644 index 0000000000000..b0b3f2dd311ba --- /dev/null +++ b/tests/cases/compiler/genericIndexedAccessVarianceComparisonResultCorrect.ts @@ -0,0 +1,26 @@ +class A { + x: string = 'A'; + y: number = 0; +} + +class B { + x: string = 'B'; + z: boolean = true; +} + +type T = Pick; + +type C = T; +type D = T; + +type C_extends_D = C extends D ? true : false; // true +type PickA_extends_PickB = Pick extends Pick ? true : false; // true +type TA_extends_TB = T extends T ? true : false; // should be true + +declare let a: T; +declare let b: T; +declare let c: C; +declare let d: D; + +b = a; // should be no error +c = d; \ No newline at end of file