From 465c0baa8dd77c81931f118f966968fe95268466 Mon Sep 17 00:00:00 2001 From: islandryu Date: Wed, 15 Dec 2021 00:08:11 +0900 Subject: [PATCH] fix(46909):Fix typePredicate in UnionType --- src/compiler/checker.ts | 10 +- .../reference/typePredicatesInUnion3.js | 68 +++++++++++++ .../reference/typePredicatesInUnion3.symbols | 82 +++++++++++++++ .../reference/typePredicatesInUnion3.types | 99 +++++++++++++++++++ .../cases/compiler/typePredicatesInUnion3.ts | 35 +++++++ 5 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/typePredicatesInUnion3.js create mode 100644 tests/baselines/reference/typePredicatesInUnion3.symbols create mode 100644 tests/baselines/reference/typePredicatesInUnion3.types create mode 100644 tests/cases/compiler/typePredicatesInUnion3.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9c5beffc61130..b2420631dc467 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24735,7 +24735,15 @@ namespace ts { // If the current type is a union type, remove all constituents that couldn't be instances of // the candidate type. If one or more constituents remain, return a union of those. if (type.flags & TypeFlags.Union) { - const assignableType = filterType(type, t => isRelated(t, candidate)); + let assignableType = filterType(type, t => isRelated(t, candidate)); + if(candidate.flags & TypeFlags.Union){ + const unionAssignableType = mapType(type, type => { + return filterType(candidate, candidate => isRelated(candidate, type)); + }); + if (!isTypeSubtypeOf(unionAssignableType, assignableType)) { + assignableType = unionAssignableType; + } + } if (!(assignableType.flags & TypeFlags.Never)) { return assignableType; } diff --git a/tests/baselines/reference/typePredicatesInUnion3.js b/tests/baselines/reference/typePredicatesInUnion3.js new file mode 100644 index 0000000000000..48849e1f656fd --- /dev/null +++ b/tests/baselines/reference/typePredicatesInUnion3.js @@ -0,0 +1,68 @@ +//// [typePredicatesInUnion3.ts] +function test1(x: string | 0) { + if (check1(x)) { + x + } + if (check2(x)) { + x + } +} + +function test2(x: string | number) { + if (check1(x)) { + x + } + if (check2(x)) { + x + } +} + + +function test3(x: "hello" | number) { + if (check1(x)) { + x + } + if (check2(x)) { + x + } +} + +function check1(x: string | number): x is ("hello" | 0) { + return x === "hello" || x === 0; +} + +function check2(x: string | number): x is ("hello" | "bye" | 0) { + return x === "hello" || x === "bye" || x === 0; +} + +//// [typePredicatesInUnion3.js] +function test1(x) { + if (check1(x)) { + x; + } + if (check2(x)) { + x; + } +} +function test2(x) { + if (check1(x)) { + x; + } + if (check2(x)) { + x; + } +} +function test3(x) { + if (check1(x)) { + x; + } + if (check2(x)) { + x; + } +} +function check1(x) { + return x === "hello" || x === 0; +} +function check2(x) { + return x === "hello" || x === "bye" || x === 0; +} diff --git a/tests/baselines/reference/typePredicatesInUnion3.symbols b/tests/baselines/reference/typePredicatesInUnion3.symbols new file mode 100644 index 0000000000000..52628275e8081 --- /dev/null +++ b/tests/baselines/reference/typePredicatesInUnion3.symbols @@ -0,0 +1,82 @@ +=== tests/cases/compiler/typePredicatesInUnion3.ts === +function test1(x: string | 0) { +>test1 : Symbol(test1, Decl(typePredicatesInUnion3.ts, 0, 0)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 0, 15)) + + if (check1(x)) { +>check1 : Symbol(check1, Decl(typePredicatesInUnion3.ts, 26, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 0, 15)) + + x +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 0, 15)) + } + if (check2(x)) { +>check2 : Symbol(check2, Decl(typePredicatesInUnion3.ts, 30, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 0, 15)) + + x +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 0, 15)) + } +} + +function test2(x: string | number) { +>test2 : Symbol(test2, Decl(typePredicatesInUnion3.ts, 7, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 9, 15)) + + if (check1(x)) { +>check1 : Symbol(check1, Decl(typePredicatesInUnion3.ts, 26, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 9, 15)) + + x +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 9, 15)) + } + if (check2(x)) { +>check2 : Symbol(check2, Decl(typePredicatesInUnion3.ts, 30, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 9, 15)) + + x +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 9, 15)) + } +} + + +function test3(x: "hello" | number) { +>test3 : Symbol(test3, Decl(typePredicatesInUnion3.ts, 16, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 19, 15)) + + if (check1(x)) { +>check1 : Symbol(check1, Decl(typePredicatesInUnion3.ts, 26, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 19, 15)) + + x +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 19, 15)) + } + if (check2(x)) { +>check2 : Symbol(check2, Decl(typePredicatesInUnion3.ts, 30, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 19, 15)) + + x +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 19, 15)) + } +} + +function check1(x: string | number): x is ("hello" | 0) { +>check1 : Symbol(check1, Decl(typePredicatesInUnion3.ts, 26, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 28, 16)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 28, 16)) + + return x === "hello" || x === 0; +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 28, 16)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 28, 16)) +} + +function check2(x: string | number): x is ("hello" | "bye" | 0) { +>check2 : Symbol(check2, Decl(typePredicatesInUnion3.ts, 30, 1)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 32, 16)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 32, 16)) + + return x === "hello" || x === "bye" || x === 0; +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 32, 16)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 32, 16)) +>x : Symbol(x, Decl(typePredicatesInUnion3.ts, 32, 16)) +} diff --git a/tests/baselines/reference/typePredicatesInUnion3.types b/tests/baselines/reference/typePredicatesInUnion3.types new file mode 100644 index 0000000000000..c1171626b98f0 --- /dev/null +++ b/tests/baselines/reference/typePredicatesInUnion3.types @@ -0,0 +1,99 @@ +=== tests/cases/compiler/typePredicatesInUnion3.ts === +function test1(x: string | 0) { +>test1 : (x: string | 0) => void +>x : string | 0 + + if (check1(x)) { +>check1(x) : boolean +>check1 : (x: string | number) => x is 0 | "hello" +>x : string | 0 + + x +>x : 0 | "hello" + } + if (check2(x)) { +>check2(x) : boolean +>check2 : (x: string | number) => x is 0 | "hello" | "bye" +>x : string | 0 + + x +>x : 0 | "hello" | "bye" + } +} + +function test2(x: string | number) { +>test2 : (x: string | number) => void +>x : string | number + + if (check1(x)) { +>check1(x) : boolean +>check1 : (x: string | number) => x is 0 | "hello" +>x : string | number + + x +>x : 0 | "hello" + } + if (check2(x)) { +>check2(x) : boolean +>check2 : (x: string | number) => x is 0 | "hello" | "bye" +>x : string | number + + x +>x : 0 | "hello" | "bye" + } +} + + +function test3(x: "hello" | number) { +>test3 : (x: "hello" | number) => void +>x : number | "hello" + + if (check1(x)) { +>check1(x) : boolean +>check1 : (x: string | number) => x is 0 | "hello" +>x : number | "hello" + + x +>x : 0 | "hello" + } + if (check2(x)) { +>check2(x) : boolean +>check2 : (x: string | number) => x is 0 | "hello" | "bye" +>x : number | "hello" + + x +>x : 0 | "hello" + } +} + +function check1(x: string | number): x is ("hello" | 0) { +>check1 : (x: string | number) => x is 0 | "hello" +>x : string | number + + return x === "hello" || x === 0; +>x === "hello" || x === 0 : boolean +>x === "hello" : boolean +>x : string | number +>"hello" : "hello" +>x === 0 : boolean +>x : string | number +>0 : 0 +} + +function check2(x: string | number): x is ("hello" | "bye" | 0) { +>check2 : (x: string | number) => x is 0 | "hello" | "bye" +>x : string | number + + return x === "hello" || x === "bye" || x === 0; +>x === "hello" || x === "bye" || x === 0 : boolean +>x === "hello" || x === "bye" : boolean +>x === "hello" : boolean +>x : string | number +>"hello" : "hello" +>x === "bye" : boolean +>x : string | number +>"bye" : "bye" +>x === 0 : boolean +>x : string | number +>0 : 0 +} diff --git a/tests/cases/compiler/typePredicatesInUnion3.ts b/tests/cases/compiler/typePredicatesInUnion3.ts new file mode 100644 index 0000000000000..6b5213d015b50 --- /dev/null +++ b/tests/cases/compiler/typePredicatesInUnion3.ts @@ -0,0 +1,35 @@ +function test1(x: string | 0) { + if (check1(x)) { + x + } + if (check2(x)) { + x + } +} + +function test2(x: string | number) { + if (check1(x)) { + x + } + if (check2(x)) { + x + } +} + + +function test3(x: "hello" | number) { + if (check1(x)) { + x + } + if (check2(x)) { + x + } +} + +function check1(x: string | number): x is ("hello" | 0) { + return x === "hello" || x === 0; +} + +function check2(x: string | number): x is ("hello" | "bye" | 0) { + return x === "hello" || x === "bye" || x === 0; +} \ No newline at end of file