From 906ebe49334a3a9c2dbd73cd3c902898bc712b66 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 19 Oct 2022 15:46:00 -0700 Subject: [PATCH] Revert structuredTypeRelatedTo change and fix isUnitLikeType (#51076) * Revert structuredTypeRelatedTo change, fix isUnitLikeType * Accept new baselines * Add regression tests * Fix formatting in test --- src/compiler/checker.ts | 5 +- .../reference/unknownControlFlow.errors.txt | 27 +++++++ .../baselines/reference/unknownControlFlow.js | 43 +++++++++++ .../reference/unknownControlFlow.symbols | 72 +++++++++++++++++++ .../reference/unknownControlFlow.types | 55 +++++++++++++- .../types/unknown/unknownControlFlow.ts | 27 +++++++ 6 files changed, 224 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a726c0757fe0..3c7b1060eae86 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19663,7 +19663,7 @@ namespace ts { // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)` if (!result && (source.flags & TypeFlags.Intersection || source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.Union)) { 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); } @@ -21630,8 +21630,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) { diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt index b49b386e32c5e..410237e3133a7 100644 --- a/tests/baselines/reference/unknownControlFlow.errors.txt +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -417,4 +417,31 @@ 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..a4979179cfb7a 100644 --- a/tests/baselines/reference/unknownControlFlow.js +++ b/tests/baselines/reference/unknownControlFlow.js @@ -400,6 +400,33 @@ 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 +769,9 @@ function doSomething2(value) { value; } } +function x(x, y) { + var r2 = y; +} //// [unknownControlFlow.d.ts] @@ -801,3 +831,16 @@ 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..d69e69fe08ee6 100644 --- a/tests/baselines/reference/unknownControlFlow.symbols +++ b/tests/baselines/reference/unknownControlFlow.symbols @@ -923,3 +923,75 @@ 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, 57)) +>T : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) + + 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 : Symbol(T, Decl(unknownControlFlow.ts, 418, 8)) +>TypeB : Symbol(TypeB, Decl(unknownControlFlow.ts, 407, 1)) +>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, 419, 89)) + +function x(x: T_AB & undefined, y: any) { +>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, 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 b2c646f56d4b5..2cf2c101c9dad 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) @@ -1025,3 +1025,54 @@ 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..082ebad7bc1f5 100644 --- a/tests/cases/conformance/types/unknown/unknownControlFlow.ts +++ b/tests/cases/conformance/types/unknown/unknownControlFlow.ts @@ -402,3 +402,30 @@ 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; +}