From 97c5587bd8f411511a64a0061e97a65712815a36 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Wed, 18 Sep 2019 19:42:28 +0100 Subject: [PATCH] Filter discriminants of type never --- src/compiler/checker.ts | 9 ++- .../discriminatedUnionTypes2.errors.txt | 23 +++++++ .../reference/discriminatedUnionTypes2.js | 31 +++++++++ .../discriminatedUnionTypes2.symbols | 66 +++++++++++++++++++ .../reference/discriminatedUnionTypes2.types | 64 ++++++++++++++++++ .../types/union/discriminatedUnionTypes2.ts | 23 +++++++ 6 files changed, 215 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f8c8817888591..ef2d198d166c2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17363,7 +17363,14 @@ namespace ts { } const propType = getTypeOfPropertyOfType(type, propName); const narrowedPropType = propType && narrowType(propType); - return propType === narrowedPropType ? type : filterType(type, t => isTypeComparableTo(getTypeOfPropertyOrIndexSignature(t, propName), narrowedPropType!)); + if (propType === narrowedPropType) { + return type; + } + const isPropertyDiscriminated = (t: Type) => { + const constituentPropType = getTypeOfPropertyOrIndexSignature(t, propName); + return (constituentPropType.flags & TypeFlags.Never) === 0 && isTypeComparableTo(constituentPropType, narrowedPropType!); + }; + return filterType(type, isPropertyDiscriminated); } function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type { diff --git a/tests/baselines/reference/discriminatedUnionTypes2.errors.txt b/tests/baselines/reference/discriminatedUnionTypes2.errors.txt index 15ed6968aeac2..db51fcb08459b 100644 --- a/tests/baselines/reference/discriminatedUnionTypes2.errors.txt +++ b/tests/baselines/reference/discriminatedUnionTypes2.errors.txt @@ -105,4 +105,27 @@ tests/cases/conformance/types/union/discriminatedUnionTypes2.ts(32,11): error TS foo; } } + + // Repro from #33448 + + type a = { + type: 'a', + data: string + } + type b = { + type: 'b', + name: string + } + type c = { + type: 'c', + other: string + } + type abc = a | b | c; + function f(problem: abc & (b | c)) { + if (problem.type === 'b') { + console.log(problem.name); + } else { + console.log(problem.other); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/discriminatedUnionTypes2.js b/tests/baselines/reference/discriminatedUnionTypes2.js index ec5b2a6b5f13f..52a74d1622b4e 100644 --- a/tests/baselines/reference/discriminatedUnionTypes2.js +++ b/tests/baselines/reference/discriminatedUnionTypes2.js @@ -93,6 +93,29 @@ function f31(foo: Foo) { foo; } } + +// Repro from #33448 + +type a = { + type: 'a', + data: string +} +type b = { + type: 'b', + name: string +} +type c = { + type: 'c', + other: string +} +type abc = a | b | c; +function f(problem: abc & (b | c)) { + if (problem.type === 'b') { + console.log(problem.name); + } else { + console.log(problem.other); + } +} //// [discriminatedUnionTypes2.js] @@ -164,3 +187,11 @@ function f31(foo) { foo; } } +function f(problem) { + if (problem.type === 'b') { + console.log(problem.name); + } + else { + console.log(problem.other); + } +} diff --git a/tests/baselines/reference/discriminatedUnionTypes2.symbols b/tests/baselines/reference/discriminatedUnionTypes2.symbols index 682c62b9be545..980f5bacdb545 100644 --- a/tests/baselines/reference/discriminatedUnionTypes2.symbols +++ b/tests/baselines/reference/discriminatedUnionTypes2.symbols @@ -273,3 +273,69 @@ function f31(foo: Foo) { } } +// Repro from #33448 + +type a = { +>a : Symbol(a, Decl(discriminatedUnionTypes2.ts, 93, 1)) + + type: 'a', +>type : Symbol(type, Decl(discriminatedUnionTypes2.ts, 97, 10)) + + data: string +>data : Symbol(data, Decl(discriminatedUnionTypes2.ts, 98, 14)) +} +type b = { +>b : Symbol(b, Decl(discriminatedUnionTypes2.ts, 100, 1)) + + type: 'b', +>type : Symbol(type, Decl(discriminatedUnionTypes2.ts, 101, 10)) + + name: string +>name : Symbol(name, Decl(discriminatedUnionTypes2.ts, 102, 14)) +} +type c = { +>c : Symbol(c, Decl(discriminatedUnionTypes2.ts, 104, 1)) + + type: 'c', +>type : Symbol(type, Decl(discriminatedUnionTypes2.ts, 105, 10)) + + other: string +>other : Symbol(other, Decl(discriminatedUnionTypes2.ts, 106, 14)) +} +type abc = a | b | c; +>abc : Symbol(abc, Decl(discriminatedUnionTypes2.ts, 108, 1)) +>a : Symbol(a, Decl(discriminatedUnionTypes2.ts, 93, 1)) +>b : Symbol(b, Decl(discriminatedUnionTypes2.ts, 100, 1)) +>c : Symbol(c, Decl(discriminatedUnionTypes2.ts, 104, 1)) + +function f(problem: abc & (b | c)) { +>f : Symbol(f, Decl(discriminatedUnionTypes2.ts, 109, 21)) +>problem : Symbol(problem, Decl(discriminatedUnionTypes2.ts, 110, 11)) +>abc : Symbol(abc, Decl(discriminatedUnionTypes2.ts, 108, 1)) +>b : Symbol(b, Decl(discriminatedUnionTypes2.ts, 100, 1)) +>c : Symbol(c, Decl(discriminatedUnionTypes2.ts, 104, 1)) + + if (problem.type === 'b') { +>problem.type : Symbol(type, Decl(discriminatedUnionTypes2.ts, 101, 10), Decl(discriminatedUnionTypes2.ts, 105, 10), Decl(discriminatedUnionTypes2.ts, 97, 10), Decl(discriminatedUnionTypes2.ts, 101, 10), Decl(discriminatedUnionTypes2.ts, 97, 10) ... and 5 more) +>problem : Symbol(problem, Decl(discriminatedUnionTypes2.ts, 110, 11)) +>type : Symbol(type, Decl(discriminatedUnionTypes2.ts, 101, 10), Decl(discriminatedUnionTypes2.ts, 105, 10), Decl(discriminatedUnionTypes2.ts, 97, 10), Decl(discriminatedUnionTypes2.ts, 101, 10), Decl(discriminatedUnionTypes2.ts, 97, 10) ... and 5 more) + + console.log(problem.name); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>problem.name : Symbol(name, Decl(discriminatedUnionTypes2.ts, 102, 14)) +>problem : Symbol(problem, Decl(discriminatedUnionTypes2.ts, 110, 11)) +>name : Symbol(name, Decl(discriminatedUnionTypes2.ts, 102, 14)) + + } else { + console.log(problem.other); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>problem.other : Symbol(other, Decl(discriminatedUnionTypes2.ts, 106, 14)) +>problem : Symbol(problem, Decl(discriminatedUnionTypes2.ts, 110, 11)) +>other : Symbol(other, Decl(discriminatedUnionTypes2.ts, 106, 14)) + } +} + diff --git a/tests/baselines/reference/discriminatedUnionTypes2.types b/tests/baselines/reference/discriminatedUnionTypes2.types index 58819d0d3495a..6c6014b4485d4 100644 --- a/tests/baselines/reference/discriminatedUnionTypes2.types +++ b/tests/baselines/reference/discriminatedUnionTypes2.types @@ -289,3 +289,67 @@ function f31(foo: Foo) { } } +// Repro from #33448 + +type a = { +>a : a + + type: 'a', +>type : "a" + + data: string +>data : string +} +type b = { +>b : b + + type: 'b', +>type : "b" + + name: string +>name : string +} +type c = { +>c : c + + type: 'c', +>type : "c" + + other: string +>other : string +} +type abc = a | b | c; +>abc : abc + +function f(problem: abc & (b | c)) { +>f : (problem: b | c | (a & b) | (a & c) | (b & c) | (c & b)) => void +>problem : b | c | (a & b) | (a & c) | (b & c) | (c & b) + + if (problem.type === 'b') { +>problem.type === 'b' : boolean +>problem.type : "b" | "c" +>problem : b | c | (a & b) | (a & c) | (b & c) | (c & b) +>type : "b" | "c" +>'b' : "b" + + console.log(problem.name); +>console.log(problem.name) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>problem.name : string +>problem : b +>name : string + + } else { + console.log(problem.other); +>console.log(problem.other) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>problem.other : string +>problem : c +>other : string + } +} + diff --git a/tests/cases/conformance/types/union/discriminatedUnionTypes2.ts b/tests/cases/conformance/types/union/discriminatedUnionTypes2.ts index a11e1447780ee..049b797f90b68 100644 --- a/tests/cases/conformance/types/union/discriminatedUnionTypes2.ts +++ b/tests/cases/conformance/types/union/discriminatedUnionTypes2.ts @@ -94,3 +94,26 @@ function f31(foo: Foo) { foo; } } + +// Repro from #33448 + +type a = { + type: 'a', + data: string +} +type b = { + type: 'b', + name: string +} +type c = { + type: 'c', + other: string +} +type abc = a | b | c; +function f(problem: abc & (b | c)) { + if (problem.type === 'b') { + console.log(problem.name); + } else { + console.log(problem.other); + } +}