From d2f324b38922a2a9355ff57c4f9dbe98c596b6a1 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 2 Aug 2021 16:12:09 -0700 Subject: [PATCH] Add test for intersection narrowing bug (#45296) --- .../reference/narrowingIntersection.js | 40 ++++++++++++ .../reference/narrowingIntersection.symbols | 63 +++++++++++++++++++ .../reference/narrowingIntersection.types | 63 +++++++++++++++++++ tests/cases/compiler/narrowingIntersection.ts | 24 +++++++ 4 files changed, 190 insertions(+) create mode 100644 tests/baselines/reference/narrowingIntersection.js create mode 100644 tests/baselines/reference/narrowingIntersection.symbols create mode 100644 tests/baselines/reference/narrowingIntersection.types create mode 100644 tests/cases/compiler/narrowingIntersection.ts diff --git a/tests/baselines/reference/narrowingIntersection.js b/tests/baselines/reference/narrowingIntersection.js new file mode 100644 index 00000000000..5f2d44f3c29 --- /dev/null +++ b/tests/baselines/reference/narrowingIntersection.js @@ -0,0 +1,40 @@ +//// [narrowingIntersection.ts] +// Somehow this being an intersection matters. +type FooAndBaz = { foo: unknown } & { baz: unknown }; + +type Disjoint = + | { readonly value: string; readonly err?: never; } + | { readonly value?: never; readonly err: FooAndBaz; }; + +function test1(result: Disjoint): string { + if (result.err) { + throw result.err; + } + // Error, should OK + return result.value; +} + +type TrivialIntersection = { a: 1 } & { a: 1 }; + +function want0(x: 0) {} + +function test2(a: 0 | TrivialIntersection) { + if (a === 0) { + want0(a); // Fails, but expect to work + } +} + +//// [narrowingIntersection.js] +function test1(result) { + if (result.err) { + throw result.err; + } + // Error, should OK + return result.value; +} +function want0(x) { } +function test2(a) { + if (a === 0) { + want0(a); // Fails, but expect to work + } +} diff --git a/tests/baselines/reference/narrowingIntersection.symbols b/tests/baselines/reference/narrowingIntersection.symbols new file mode 100644 index 00000000000..7d07d94bb16 --- /dev/null +++ b/tests/baselines/reference/narrowingIntersection.symbols @@ -0,0 +1,63 @@ +=== tests/cases/compiler/narrowingIntersection.ts === +// Somehow this being an intersection matters. +type FooAndBaz = { foo: unknown } & { baz: unknown }; +>FooAndBaz : Symbol(FooAndBaz, Decl(narrowingIntersection.ts, 0, 0)) +>foo : Symbol(foo, Decl(narrowingIntersection.ts, 1, 18)) +>baz : Symbol(baz, Decl(narrowingIntersection.ts, 1, 37)) + +type Disjoint = +>Disjoint : Symbol(Disjoint, Decl(narrowingIntersection.ts, 1, 53)) + + | { readonly value: string; readonly err?: never; } +>value : Symbol(value, Decl(narrowingIntersection.ts, 4, 4)) +>err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28)) + + | { readonly value?: never; readonly err: FooAndBaz; }; +>value : Symbol(value, Decl(narrowingIntersection.ts, 5, 4)) +>err : Symbol(err, Decl(narrowingIntersection.ts, 5, 28)) +>FooAndBaz : Symbol(FooAndBaz, Decl(narrowingIntersection.ts, 0, 0)) + +function test1(result: Disjoint): string { +>test1 : Symbol(test1, Decl(narrowingIntersection.ts, 5, 56)) +>result : Symbol(result, Decl(narrowingIntersection.ts, 7, 15)) +>Disjoint : Symbol(Disjoint, Decl(narrowingIntersection.ts, 1, 53)) + + if (result.err) { +>result.err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28), Decl(narrowingIntersection.ts, 5, 28)) +>result : Symbol(result, Decl(narrowingIntersection.ts, 7, 15)) +>err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28), Decl(narrowingIntersection.ts, 5, 28)) + + throw result.err; +>result.err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28), Decl(narrowingIntersection.ts, 5, 28)) +>result : Symbol(result, Decl(narrowingIntersection.ts, 7, 15)) +>err : Symbol(err, Decl(narrowingIntersection.ts, 4, 28), Decl(narrowingIntersection.ts, 5, 28)) + } + // Error, should OK + return result.value; +>result.value : Symbol(value, Decl(narrowingIntersection.ts, 4, 4), Decl(narrowingIntersection.ts, 5, 4)) +>result : Symbol(result, Decl(narrowingIntersection.ts, 7, 15)) +>value : Symbol(value, Decl(narrowingIntersection.ts, 4, 4), Decl(narrowingIntersection.ts, 5, 4)) +} + +type TrivialIntersection = { a: 1 } & { a: 1 }; +>TrivialIntersection : Symbol(TrivialIntersection, Decl(narrowingIntersection.ts, 13, 1)) +>a : Symbol(a, Decl(narrowingIntersection.ts, 15, 28)) +>a : Symbol(a, Decl(narrowingIntersection.ts, 15, 39)) + +function want0(x: 0) {} +>want0 : Symbol(want0, Decl(narrowingIntersection.ts, 15, 47)) +>x : Symbol(x, Decl(narrowingIntersection.ts, 17, 15)) + +function test2(a: 0 | TrivialIntersection) { +>test2 : Symbol(test2, Decl(narrowingIntersection.ts, 17, 23)) +>a : Symbol(a, Decl(narrowingIntersection.ts, 19, 15)) +>TrivialIntersection : Symbol(TrivialIntersection, Decl(narrowingIntersection.ts, 13, 1)) + + if (a === 0) { +>a : Symbol(a, Decl(narrowingIntersection.ts, 19, 15)) + + want0(a); // Fails, but expect to work +>want0 : Symbol(want0, Decl(narrowingIntersection.ts, 15, 47)) +>a : Symbol(a, Decl(narrowingIntersection.ts, 19, 15)) + } +} diff --git a/tests/baselines/reference/narrowingIntersection.types b/tests/baselines/reference/narrowingIntersection.types new file mode 100644 index 00000000000..ba44c888c80 --- /dev/null +++ b/tests/baselines/reference/narrowingIntersection.types @@ -0,0 +1,63 @@ +=== tests/cases/compiler/narrowingIntersection.ts === +// Somehow this being an intersection matters. +type FooAndBaz = { foo: unknown } & { baz: unknown }; +>FooAndBaz : FooAndBaz +>foo : unknown +>baz : unknown + +type Disjoint = +>Disjoint : Disjoint + + | { readonly value: string; readonly err?: never; } +>value : string +>err : never + + | { readonly value?: never; readonly err: FooAndBaz; }; +>value : never +>err : FooAndBaz + +function test1(result: Disjoint): string { +>test1 : (result: Disjoint) => string +>result : Disjoint + + if (result.err) { +>result.err : FooAndBaz +>result : Disjoint +>err : FooAndBaz + + throw result.err; +>result.err : FooAndBaz +>result : Disjoint +>err : FooAndBaz + } + // Error, should OK + return result.value; +>result.value : string +>result : Disjoint +>value : string +} + +type TrivialIntersection = { a: 1 } & { a: 1 }; +>TrivialIntersection : TrivialIntersection +>a : 1 +>a : 1 + +function want0(x: 0) {} +>want0 : (x: 0) => void +>x : 0 + +function test2(a: 0 | TrivialIntersection) { +>test2 : (a: 0 | TrivialIntersection) => void +>a : 0 | TrivialIntersection + + if (a === 0) { +>a === 0 : boolean +>a : 0 | TrivialIntersection +>0 : 0 + + want0(a); // Fails, but expect to work +>want0(a) : void +>want0 : (x: 0) => void +>a : 0 + } +} diff --git a/tests/cases/compiler/narrowingIntersection.ts b/tests/cases/compiler/narrowingIntersection.ts new file mode 100644 index 00000000000..a7da3bcb91b --- /dev/null +++ b/tests/cases/compiler/narrowingIntersection.ts @@ -0,0 +1,24 @@ +// Somehow this being an intersection matters. +type FooAndBaz = { foo: unknown } & { baz: unknown }; + +type Disjoint = + | { readonly value: string; readonly err?: never; } + | { readonly value?: never; readonly err: FooAndBaz; }; + +function test1(result: Disjoint): string { + if (result.err) { + throw result.err; + } + // Error, should OK + return result.value; +} + +type TrivialIntersection = { a: 1 } & { a: 1 }; + +function want0(x: 0) {} + +function test2(a: 0 | TrivialIntersection) { + if (a === 0) { + want0(a); // Fails, but expect to work + } +} \ No newline at end of file