From 8ff245fb9f1e85cac6ceb51b613966755084722a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 5 Oct 2022 13:38:46 -0700 Subject: [PATCH 1/4] Revert structuredTypeRelatedTo change, fix isUnitLikeType --- src/compiler/checker.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e4f940238cd3..f36f62f41668c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19679,7 +19679,7 @@ namespace ts { // the type param can be compared with itself in the target (with the influence of its constraint to match other parts) // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)` const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union)); - if (constraint && !(constraint.flags & TypeFlags.Never) && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself + if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState); } @@ -21626,8 +21626,7 @@ namespace ts { } function isUnitLikeType(type: Type): boolean { - return type.flags & TypeFlags.Intersection ? some((type as IntersectionType).types, isUnitType) : - !!(type.flags & TypeFlags.Unit); + return isUnitType(getBaseConstraintOrType(type)); } function extractUnitType(type: Type) { From 8fd5e4c4a0b5d8d2c203ac32a9338dfab307c037 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 5 Oct 2022 13:39:39 -0700 Subject: [PATCH 2/4] Accept new baselines --- tests/baselines/reference/unknownControlFlow.types | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index b2c646f56d4b5..ec318f96b231c 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -853,7 +853,7 @@ function fx2(value: T & ({} | null)) { >42 : 42 value; // T & {} ->value : T & {} +>value : T & ({} | null) } else { value; // T & ({} | null) @@ -872,7 +872,7 @@ function fx3(value: T & ({} | null)) { >42 : 42 value; // T & {} ->value : T & {} +>value : T & ({} | null) } else { value; // T & ({} | null) From e363198a94e86ee11ac059b53d2e2a697bafd699 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 5 Oct 2022 13:45:04 -0700 Subject: [PATCH 3/4] Add regression tests --- .../reference/unknownControlFlow.errors.txt | 29 +++++++ .../baselines/reference/unknownControlFlow.js | 48 ++++++++++++ .../reference/unknownControlFlow.symbols | 76 +++++++++++++++++++ .../reference/unknownControlFlow.types | 53 +++++++++++++ .../types/unknown/unknownControlFlow.ts | 29 +++++++ 5 files changed, 235 insertions(+) diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt index b49b386e32c5e..4d567994eaa9f 100644 --- a/tests/baselines/reference/unknownControlFlow.errors.txt +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -417,4 +417,33 @@ tests/cases/conformance/types/unknown/unknownControlFlow.ts(293,5): error TS2345 value; } } + + // Repro from #51009 + + type TypeA = { + A: 'A', + B: 'B', + } + + type TypeB = { + A: 'A', + B: 'B', + C: 'C', + } + + type R = + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + + type R2 = + T extends keyof TypeA ? + T extends keyof TypeB ? + [TypeA[T], TypeB[T]] : never: never + + // Repro from #51041 + + type AB = "A" | "B" + + function x(x: T_AB & undefined, y: any) { + let r2: never = y as T_AB & undefined; + } \ No newline at end of file diff --git a/tests/baselines/reference/unknownControlFlow.js b/tests/baselines/reference/unknownControlFlow.js index a67326b14b1af..07a324844dfa0 100644 --- a/tests/baselines/reference/unknownControlFlow.js +++ b/tests/baselines/reference/unknownControlFlow.js @@ -400,6 +400,35 @@ function doSomething2(value: unknown): void { value; } } + +// Repro from #51009 + +type TypeA = { + A: 'A', + B: 'B', +} + +type TypeB = { + A: 'A', + B: 'B', + C: 'C', +} + +type R = + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + +type R2 = + T extends keyof TypeA ? + T extends keyof TypeB ? + [TypeA[T], TypeB[T]] : never: never + +// Repro from #51041 + +type AB = "A" | "B" + +function x(x: T_AB & undefined, y: any) { + let r2: never = y as T_AB & undefined; +} //// [unknownControlFlow.js] @@ -742,6 +771,9 @@ function doSomething2(value) { value; } } +function x(x, y) { + var r2 = y; +} //// [unknownControlFlow.d.ts] @@ -801,3 +833,19 @@ declare function fx10(x: string | number, y: number): void; declare function SendBlob(encoding: unknown): void; declare function doSomething1(value: T): T; declare function doSomething2(value: unknown): void; +type TypeA = { + A: 'A'; + B: 'B'; +}; +type TypeB = { + A: 'A'; + B: 'B'; + C: 'C'; +}; +type R = T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never; +type R2 = T extends keyof TypeA ? T extends keyof TypeB ? [ + TypeA[T], + TypeB[T] +] : never : never; +type AB = "A" | "B"; +declare function x(x: T_AB & undefined, y: any): void; diff --git a/tests/baselines/reference/unknownControlFlow.symbols b/tests/baselines/reference/unknownControlFlow.symbols index 1644306428ae7..eaebd2bcbbc55 100644 --- a/tests/baselines/reference/unknownControlFlow.symbols +++ b/tests/baselines/reference/unknownControlFlow.symbols @@ -923,3 +923,79 @@ function doSomething2(value: unknown): void { } } +// Repro from #51009 + +type TypeA = { +>TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) + + A: 'A', +>A : Symbol(A, Decl(unknownControlFlow.ts, 404, 14)) + + B: 'B', +>B : Symbol(B, Decl(unknownControlFlow.ts, 405, 11)) +} + +type TypeB = { +>TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) + + A: 'A', +>A : Symbol(A, Decl(unknownControlFlow.ts, 409, 14)) + + B: 'B', +>B : Symbol(B, Decl(unknownControlFlow.ts, 410, 11)) + + C: 'C', +>C : Symbol(C, Decl(unknownControlFlow.ts, 411, 11)) +} + +type R = +>R : Symbol(R, Decl(unknownControlFlow.ts, 413, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 415, 7)) +>TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) + + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never +>T : Symbol(T, Decl(unknownControlFlow.ts, 415, 7)) +>TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) +>TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 415, 7)) +>TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 415, 7)) + +type R2 = +>R2 : Symbol(R2, Decl(unknownControlFlow.ts, 416, 56)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) + + T extends keyof TypeA ? +>T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) +>TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) + + T extends keyof TypeB ? +>T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) +>TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) + + [TypeA[T], TypeB[T]] : never: never +>TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) +>TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) + +// Repro from #51041 + +type AB = "A" | "B" +>AB : Symbol(AB, Decl(unknownControlFlow.ts, 421, 43)) + +function x(x: T_AB & undefined, y: any) { +>x : Symbol(x, Decl(unknownControlFlow.ts, 425, 19)) +>T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 427, 11)) +>AB : Symbol(AB, Decl(unknownControlFlow.ts, 421, 43)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 427, 28)) +>T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 427, 11)) +>y : Symbol(y, Decl(unknownControlFlow.ts, 427, 48)) + + let r2: never = y as T_AB & undefined; +>r2 : Symbol(r2, Decl(unknownControlFlow.ts, 428, 7)) +>y : Symbol(y, Decl(unknownControlFlow.ts, 427, 48)) +>T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 427, 11)) +} + diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index ec318f96b231c..d45438d6e521c 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -1025,3 +1025,56 @@ function doSomething2(value: unknown): void { } } +// Repro from #51009 + +type TypeA = { +>TypeA : { A: 'A'; B: 'B'; } + + A: 'A', +>A : "A" + + B: 'B', +>B : "B" +} + +type TypeB = { +>TypeB : { A: 'A'; B: 'B'; C: 'C'; } + + A: 'A', +>A : "A" + + B: 'B', +>B : "B" + + C: 'C', +>C : "C" +} + +type R = +>R : R + + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + +type R2 = +>R2 : R2 + + T extends keyof TypeA ? + T extends keyof TypeB ? + [TypeA[T], TypeB[T]] : never: never + +// Repro from #51041 + +type AB = "A" | "B" +>AB : "A" | "B" + +function x(x: T_AB & undefined, y: any) { +>x : (x: T_AB & undefined, y: any) => void +>x : T_AB & undefined +>y : any + + let r2: never = y as T_AB & undefined; +>r2 : never +>y as T_AB & undefined : T_AB & undefined +>y : any +} + diff --git a/tests/cases/conformance/types/unknown/unknownControlFlow.ts b/tests/cases/conformance/types/unknown/unknownControlFlow.ts index da9bb6fe65e8e..03cce40d9a1f4 100644 --- a/tests/cases/conformance/types/unknown/unknownControlFlow.ts +++ b/tests/cases/conformance/types/unknown/unknownControlFlow.ts @@ -402,3 +402,32 @@ function doSomething2(value: unknown): void { value; } } + +// Repro from #51009 + +type TypeA = { + A: 'A', + B: 'B', +} + +type TypeB = { + A: 'A', + B: 'B', + C: 'C', +} + +type R = + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + +type R2 = + T extends keyof TypeA ? + T extends keyof TypeB ? + [TypeA[T], TypeB[T]] : never: never + +// Repro from #51041 + +type AB = "A" | "B" + +function x(x: T_AB & undefined, y: any) { + let r2: never = y as T_AB & undefined; +} From 804722902a43eb1c1ca127f4b6fc48faff2b4599 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 18 Oct 2022 07:37:14 -0700 Subject: [PATCH 4/4] Fix formatting in test --- .../reference/unknownControlFlow.errors.txt | 8 ++--- .../baselines/reference/unknownControlFlow.js | 13 +++----- .../reference/unknownControlFlow.symbols | 32 ++++++++----------- .../reference/unknownControlFlow.types | 8 ++--- .../types/unknown/unknownControlFlow.ts | 8 ++--- 5 files changed, 27 insertions(+), 42 deletions(-) diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt index 4d567994eaa9f..410237e3133a7 100644 --- a/tests/baselines/reference/unknownControlFlow.errors.txt +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -432,16 +432,14 @@ tests/cases/conformance/types/unknown/unknownControlFlow.ts(293,5): error TS2345 } type R = - T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never; type R2 = - T extends keyof TypeA ? - T extends keyof TypeB ? - [TypeA[T], TypeB[T]] : never: never + T extends keyof TypeA ? T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never : never; // Repro from #51041 - type AB = "A" | "B" + type AB = "A" | "B"; function x(x: T_AB & undefined, y: any) { let r2: never = y as T_AB & undefined; diff --git a/tests/baselines/reference/unknownControlFlow.js b/tests/baselines/reference/unknownControlFlow.js index 07a324844dfa0..a4979179cfb7a 100644 --- a/tests/baselines/reference/unknownControlFlow.js +++ b/tests/baselines/reference/unknownControlFlow.js @@ -415,16 +415,14 @@ type TypeB = { } type R = - T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never; type R2 = - T extends keyof TypeA ? - T extends keyof TypeB ? - [TypeA[T], TypeB[T]] : never: never + T extends keyof TypeA ? T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never : never; // Repro from #51041 -type AB = "A" | "B" +type AB = "A" | "B"; function x(x: T_AB & undefined, y: any) { let r2: never = y as T_AB & undefined; @@ -843,9 +841,6 @@ type TypeB = { C: 'C'; }; type R = T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never; -type R2 = T extends keyof TypeA ? T extends keyof TypeB ? [ - TypeA[T], - TypeB[T] -] : never : never; +type R2 = T extends keyof TypeA ? T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never : never; type AB = "A" | "B"; declare function x(x: T_AB & undefined, y: any): void; diff --git a/tests/baselines/reference/unknownControlFlow.symbols b/tests/baselines/reference/unknownControlFlow.symbols index eaebd2bcbbc55..d69e69fe08ee6 100644 --- a/tests/baselines/reference/unknownControlFlow.symbols +++ b/tests/baselines/reference/unknownControlFlow.symbols @@ -953,7 +953,7 @@ type R = >T : Symbol(T, Decl(unknownControlFlow.ts, 415, 7)) >TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) - T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never; >T : Symbol(T, Decl(unknownControlFlow.ts, 415, 7)) >TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) >TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) @@ -962,19 +962,15 @@ type R = >T : Symbol(T, Decl(unknownControlFlow.ts, 415, 7)) type R2 = ->R2 : Symbol(R2, Decl(unknownControlFlow.ts, 416, 56)) +>R2 : Symbol(R2, Decl(unknownControlFlow.ts, 416, 57)) >T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) >PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) - T extends keyof TypeA ? + T extends keyof TypeA ? T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never : never; >T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) >TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) - - T extends keyof TypeB ? >T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) >TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) - - [TypeA[T], TypeB[T]] : never: never >TypeA : Symbol(TypeA, Decl(unknownControlFlow.ts, 400, 1)) >T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) >TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) @@ -982,20 +978,20 @@ type R2 = // Repro from #51041 -type AB = "A" | "B" ->AB : Symbol(AB, Decl(unknownControlFlow.ts, 421, 43)) +type AB = "A" | "B"; +>AB : Symbol(AB, Decl(unknownControlFlow.ts, 419, 89)) function x(x: T_AB & undefined, y: any) { ->x : Symbol(x, Decl(unknownControlFlow.ts, 425, 19)) ->T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 427, 11)) ->AB : Symbol(AB, Decl(unknownControlFlow.ts, 421, 43)) ->x : Symbol(x, Decl(unknownControlFlow.ts, 427, 28)) ->T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 427, 11)) ->y : Symbol(y, Decl(unknownControlFlow.ts, 427, 48)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 423, 20)) +>T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 425, 11)) +>AB : Symbol(AB, Decl(unknownControlFlow.ts, 419, 89)) +>x : Symbol(x, Decl(unknownControlFlow.ts, 425, 28)) +>T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 425, 11)) +>y : Symbol(y, Decl(unknownControlFlow.ts, 425, 48)) let r2: never = y as T_AB & undefined; ->r2 : Symbol(r2, Decl(unknownControlFlow.ts, 428, 7)) ->y : Symbol(y, Decl(unknownControlFlow.ts, 427, 48)) ->T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 427, 11)) +>r2 : Symbol(r2, Decl(unknownControlFlow.ts, 426, 7)) +>y : Symbol(y, Decl(unknownControlFlow.ts, 425, 48)) +>T_AB : Symbol(T_AB, Decl(unknownControlFlow.ts, 425, 11)) } diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index d45438d6e521c..2cf2c101c9dad 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -1053,18 +1053,16 @@ type TypeB = { type R = >R : R - T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never; type R2 = >R2 : R2 - T extends keyof TypeA ? - T extends keyof TypeB ? - [TypeA[T], TypeB[T]] : never: never + T extends keyof TypeA ? T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never : never; // Repro from #51041 -type AB = "A" | "B" +type AB = "A" | "B"; >AB : "A" | "B" function x(x: T_AB & undefined, y: any) { diff --git a/tests/cases/conformance/types/unknown/unknownControlFlow.ts b/tests/cases/conformance/types/unknown/unknownControlFlow.ts index 03cce40d9a1f4..082ebad7bc1f5 100644 --- a/tests/cases/conformance/types/unknown/unknownControlFlow.ts +++ b/tests/cases/conformance/types/unknown/unknownControlFlow.ts @@ -417,16 +417,14 @@ type TypeB = { } type R = - T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never + T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never; type R2 = - T extends keyof TypeA ? - T extends keyof TypeB ? - [TypeA[T], TypeB[T]] : never: never + T extends keyof TypeA ? T extends keyof TypeB ? [TypeA[T], TypeB[T]] : never : never; // Repro from #51041 -type AB = "A" | "B" +type AB = "A" | "B"; function x(x: T_AB & undefined, y: any) { let r2: never = y as T_AB & undefined;