From 6bd34243938e82108c6c605830180287539683ef Mon Sep 17 00:00:00 2001 From: Titian Cernicova-Dragomir Date: Thu, 26 Dec 2019 20:27:49 +0200 Subject: [PATCH] Fixed issue where non null assertion caused getFlowTypeOfReference to return the declared type if the type was already narrowed to never. This was caused by the fact that getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull) will return never both if resultType was already never and if resultType does not contain undefined or null. In the latter case returning the declaring type is correct, in the former case this causes something narrowed to never to still be typed as the original declared type. --- src/compiler/checker.ts | 2 +- .../exhaustiveSwitchStatements1.errors.txt | 18 ++++++++++-- .../reference/exhaustiveSwitchStatements1.js | 27 ++++++++++++++++- .../exhaustiveSwitchStatements1.symbols | 23 +++++++++++++++ .../exhaustiveSwitchStatements1.types | 29 +++++++++++++++++++ .../exhaustiveSwitchStatements1.ts | 12 ++++++++ 6 files changed, 107 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b155f3dfab106..b22895b9b808f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22185,7 +22185,7 @@ namespace ts { // on empty arrays are possible without implicit any errors and new element types can be inferred without // type mismatch errors. const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? autoArrayType : finalizeEvolvingArrayType(evolvedType); - if (resultType === unreachableNeverType || reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { + if (resultType === unreachableNeverType || reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && !(resultType.flags & TypeFlags.Never) && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) { return declaredType; } return resultType; diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt b/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt index a8e02b04aa8db..1f1bc0eca312d 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt @@ -1,8 +1,9 @@ tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(7,9): error TS7027: Unreachable code detected. tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(235,5): error TS2367: This condition will always return 'false' since the types 'keyof O' and '"c"' have no overlap. +tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(247,10): error TS2339: Property 'kind' does not exist on type 'never'. -==== tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts (2 errors) ==== +==== tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts (3 errors) ==== function f1(x: 1 | 2): string { if (!!true) { switch (x) { @@ -244,4 +245,17 @@ tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(235,5): error !!! error TS2367: This condition will always return 'false' since the types 'keyof O' and '"c"' have no overlap. return o[k]; } - \ No newline at end of file + + // Repro from #35431 + type A = { kind: "abc" } | { kind: "def" }; + + function f35431(a: A) { + switch (a.kind) { + case "abc": + case "def": return; + default: + a!.kind; // Error expected + ~~~~ +!!! error TS2339: Property 'kind' does not exist on type 'never'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.js b/tests/baselines/reference/exhaustiveSwitchStatements1.js index d172b00b847d7..625265466a4f1 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.js +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.js @@ -236,7 +236,18 @@ function ff(o: O, k: K) { k === 'c'; // Error return o[k]; } - + +// Repro from #35431 +type A = { kind: "abc" } | { kind: "def" }; + +function f35431(a: A) { + switch (a.kind) { + case "abc": + case "def": return; + default: + a!.kind; // Error expected + } +} //// [exhaustiveSwitchStatements1.js] "use strict"; @@ -453,6 +464,14 @@ function ff(o, k) { k === 'c'; // Error return o[k]; } +function f35431(a) { + switch (a.kind) { + case "abc": + case "def": return; + default: + a.kind; // Error expected + } +} //// [exhaustiveSwitchStatements1.d.ts] @@ -524,3 +543,9 @@ declare type O = { }; declare type K = keyof O | 'c'; declare function ff(o: O, k: K): number; +declare type A = { + kind: "abc"; +} | { + kind: "def"; +}; +declare function f35431(a: A): void; diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.symbols b/tests/baselines/reference/exhaustiveSwitchStatements1.symbols index 6ee1e4a98897a..a9c2d820f2bfc 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.symbols +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.symbols @@ -603,3 +603,26 @@ function ff(o: O, k: K) { >k : Symbol(k, Decl(exhaustiveSwitchStatements1.ts, 229, 17)) } +// Repro from #35431 +type A = { kind: "abc" } | { kind: "def" }; +>A : Symbol(A, Decl(exhaustiveSwitchStatements1.ts, 236, 1)) +>kind : Symbol(kind, Decl(exhaustiveSwitchStatements1.ts, 239, 10)) +>kind : Symbol(kind, Decl(exhaustiveSwitchStatements1.ts, 239, 28)) + +function f35431(a: A) { +>f35431 : Symbol(f35431, Decl(exhaustiveSwitchStatements1.ts, 239, 43)) +>a : Symbol(a, Decl(exhaustiveSwitchStatements1.ts, 241, 16)) +>A : Symbol(A, Decl(exhaustiveSwitchStatements1.ts, 236, 1)) + + switch (a.kind) { +>a.kind : Symbol(kind, Decl(exhaustiveSwitchStatements1.ts, 239, 10), Decl(exhaustiveSwitchStatements1.ts, 239, 28)) +>a : Symbol(a, Decl(exhaustiveSwitchStatements1.ts, 241, 16)) +>kind : Symbol(kind, Decl(exhaustiveSwitchStatements1.ts, 239, 10), Decl(exhaustiveSwitchStatements1.ts, 239, 28)) + + case "abc": + case "def": return; + default: + a!.kind; // Error expected +>a : Symbol(a, Decl(exhaustiveSwitchStatements1.ts, 241, 16)) + } +} diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.types b/tests/baselines/reference/exhaustiveSwitchStatements1.types index 043bb099f0161..4944989ed1d6d 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.types +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.types @@ -704,3 +704,32 @@ function ff(o: O, k: K) { >k : keyof O } +// Repro from #35431 +type A = { kind: "abc" } | { kind: "def" }; +>A : A +>kind : "abc" +>kind : "def" + +function f35431(a: A) { +>f35431 : (a: A) => void +>a : A + + switch (a.kind) { +>a.kind : "abc" | "def" +>a : A +>kind : "abc" | "def" + + case "abc": +>"abc" : "abc" + + case "def": return; +>"def" : "def" + + default: + a!.kind; // Error expected +>a!.kind : any +>a! : never +>a : never +>kind : any + } +} diff --git a/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts b/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts index 9c03f039a8ba0..2bba269b89d1c 100644 --- a/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts +++ b/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts @@ -239,3 +239,15 @@ function ff(o: O, k: K) { k === 'c'; // Error return o[k]; } + +// Repro from #35431 +type A = { kind: "abc" } | { kind: "def" }; + +function f35431(a: A) { + switch (a.kind) { + case "abc": + case "def": return; + default: + a!.kind; // Error expected + } +} \ No newline at end of file