From d9ef328a734f215ef57918779b591428ce73c862 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 27 Jul 2021 10:58:51 -0700 Subject: [PATCH] Negative cases of getNarrowedType that match the exact type should be filtered out, even when generic --- src/compiler/checker.ts | 3 ++ .../genericCapturingFunctionNarrowing.js | 28 +++++++++++ .../genericCapturingFunctionNarrowing.symbols | 46 +++++++++++++++++++ .../genericCapturingFunctionNarrowing.types | 43 +++++++++++++++++ ...TypeAtReturnPositionsInaccurate.errors.txt | 10 +--- ...kinfoTypeAtReturnPositionsInaccurate.types | 4 +- .../genericCapturingFunctionNarrowing.ts | 13 ++++++ 7 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/genericCapturingFunctionNarrowing.js create mode 100644 tests/baselines/reference/genericCapturingFunctionNarrowing.symbols create mode 100644 tests/baselines/reference/genericCapturingFunctionNarrowing.types create mode 100644 tests/cases/compiler/genericCapturingFunctionNarrowing.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 718f2edaef5ec..413b38af9ae62 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24004,6 +24004,9 @@ namespace ts { if (!isRelated(t, candidate)) { return true; } + if (candidate === t) { + return false; + } const constraint = getBaseConstraintOfType(t); if (constraint && constraint !== t) { return !isRelated(constraint, candidate); diff --git a/tests/baselines/reference/genericCapturingFunctionNarrowing.js b/tests/baselines/reference/genericCapturingFunctionNarrowing.js new file mode 100644 index 0000000000000..ee037bda04fe5 --- /dev/null +++ b/tests/baselines/reference/genericCapturingFunctionNarrowing.js @@ -0,0 +1,28 @@ +//// [genericCapturingFunctionNarrowing.ts] +function needsToNarrowTheType(thing: First | Second) { + if (hasAFoo(thing)) { + console.log(thing.foo); + } + else { + // I would expect this to work because the type should be narrowed in this branch to `Second` + console.log(thing.bar); // Error: Property 'bar' does not exist on type 'First | Second'. + } + + function hasAFoo(value: First | Second): value is First { + return "foo" in value; + } +} + +//// [genericCapturingFunctionNarrowing.js] +function needsToNarrowTheType(thing) { + if (hasAFoo(thing)) { + console.log(thing.foo); + } + else { + // I would expect this to work because the type should be narrowed in this branch to `Second` + console.log(thing.bar); // Error: Property 'bar' does not exist on type 'First | Second'. + } + function hasAFoo(value) { + return "foo" in value; + } +} diff --git a/tests/baselines/reference/genericCapturingFunctionNarrowing.symbols b/tests/baselines/reference/genericCapturingFunctionNarrowing.symbols new file mode 100644 index 0000000000000..aa6ea24d8665e --- /dev/null +++ b/tests/baselines/reference/genericCapturingFunctionNarrowing.symbols @@ -0,0 +1,46 @@ +=== tests/cases/compiler/genericCapturingFunctionNarrowing.ts === +function needsToNarrowTheType(thing: First | Second) { +>needsToNarrowTheType : Symbol(needsToNarrowTheType, Decl(genericCapturingFunctionNarrowing.ts, 0, 0)) +>First : Symbol(First, Decl(genericCapturingFunctionNarrowing.ts, 0, 30)) +>foo : Symbol(foo, Decl(genericCapturingFunctionNarrowing.ts, 0, 45)) +>Second : Symbol(Second, Decl(genericCapturingFunctionNarrowing.ts, 0, 60)) +>bar : Symbol(bar, Decl(genericCapturingFunctionNarrowing.ts, 0, 77)) +>thing : Symbol(thing, Decl(genericCapturingFunctionNarrowing.ts, 0, 93)) +>First : Symbol(First, Decl(genericCapturingFunctionNarrowing.ts, 0, 30)) +>Second : Symbol(Second, Decl(genericCapturingFunctionNarrowing.ts, 0, 60)) + + if (hasAFoo(thing)) { +>hasAFoo : Symbol(hasAFoo, Decl(genericCapturingFunctionNarrowing.ts, 7, 5)) +>thing : Symbol(thing, Decl(genericCapturingFunctionNarrowing.ts, 0, 93)) + + console.log(thing.foo); +>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, --, --)) +>thing.foo : Symbol(foo, Decl(genericCapturingFunctionNarrowing.ts, 0, 45)) +>thing : Symbol(thing, Decl(genericCapturingFunctionNarrowing.ts, 0, 93)) +>foo : Symbol(foo, Decl(genericCapturingFunctionNarrowing.ts, 0, 45)) + } + else { + // I would expect this to work because the type should be narrowed in this branch to `Second` + console.log(thing.bar); // Error: Property 'bar' does not exist on type 'First | Second'. +>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, --, --)) +>thing.bar : Symbol(bar, Decl(genericCapturingFunctionNarrowing.ts, 0, 77)) +>thing : Symbol(thing, Decl(genericCapturingFunctionNarrowing.ts, 0, 93)) +>bar : Symbol(bar, Decl(genericCapturingFunctionNarrowing.ts, 0, 77)) + } + + function hasAFoo(value: First | Second): value is First { +>hasAFoo : Symbol(hasAFoo, Decl(genericCapturingFunctionNarrowing.ts, 7, 5)) +>value : Symbol(value, Decl(genericCapturingFunctionNarrowing.ts, 9, 21)) +>First : Symbol(First, Decl(genericCapturingFunctionNarrowing.ts, 0, 30)) +>Second : Symbol(Second, Decl(genericCapturingFunctionNarrowing.ts, 0, 60)) +>value : Symbol(value, Decl(genericCapturingFunctionNarrowing.ts, 9, 21)) +>First : Symbol(First, Decl(genericCapturingFunctionNarrowing.ts, 0, 30)) + + return "foo" in value; +>value : Symbol(value, Decl(genericCapturingFunctionNarrowing.ts, 9, 21)) + } +} diff --git a/tests/baselines/reference/genericCapturingFunctionNarrowing.types b/tests/baselines/reference/genericCapturingFunctionNarrowing.types new file mode 100644 index 0000000000000..66d1cd06d7f7b --- /dev/null +++ b/tests/baselines/reference/genericCapturingFunctionNarrowing.types @@ -0,0 +1,43 @@ +=== tests/cases/compiler/genericCapturingFunctionNarrowing.ts === +function needsToNarrowTheType(thing: First | Second) { +>needsToNarrowTheType : (thing: First | Second) => void +>foo : string +>bar : string +>thing : First | Second + + if (hasAFoo(thing)) { +>hasAFoo(thing) : boolean +>hasAFoo : (value: First | Second) => value is First +>thing : First | Second + + console.log(thing.foo); +>console.log(thing.foo) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>thing.foo : string +>thing : First +>foo : string + } + else { + // I would expect this to work because the type should be narrowed in this branch to `Second` + console.log(thing.bar); // Error: Property 'bar' does not exist on type 'First | Second'. +>console.log(thing.bar) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>thing.bar : string +>thing : Second +>bar : string + } + + function hasAFoo(value: First | Second): value is First { +>hasAFoo : (value: First | Second) => value is First +>value : First | Second + + return "foo" in value; +>"foo" in value : boolean +>"foo" : "foo" +>value : First | Second + } +} diff --git a/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.errors.txt b/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.errors.txt index 15d2835790b00..3791e82e28c0b 100644 --- a/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.errors.txt +++ b/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.errors.txt @@ -1,11 +1,8 @@ tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts(33,15): error TS2339: Property 'numExclusive' does not exist on type 'NumClass | StrClass'. Property 'numExclusive' does not exist on type 'StrClass'. -tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts(101,11): error TS2322: Type 'Program | T' is not assignable to type 'Program'. - Type 'T' is not assignable to type 'Program'. - Property 'state' is missing in type 'BuilderProgram' but required in type 'Program'. -==== tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts (2 errors) ==== +==== tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts (1 errors) ==== class NumClass { private value!: T; public get(): T { @@ -110,9 +107,4 @@ tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts(101,11): error declare function isBuilderProgram(program: Program | T): program is T; export function listFiles(program: Program | T) { const x: Program = isBuilderProgram(program) ? program.getProgram() : program; - ~ -!!! error TS2322: Type 'Program | T' is not assignable to type 'Program'. -!!! error TS2322: Type 'T' is not assignable to type 'Program'. -!!! error TS2322: Property 'state' is missing in type 'BuilderProgram' but required in type 'Program'. -!!! related TS2728 tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts:97:5: 'state' is declared here. } \ No newline at end of file diff --git a/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.types b/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.types index 9d0e23dc6a884..1782050b8f261 100644 --- a/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.types +++ b/tests/baselines/reference/quickinfoTypeAtReturnPositionsInaccurate.types @@ -226,7 +226,7 @@ export function listFiles(program: Program | T) { const x: Program = isBuilderProgram(program) ? program.getProgram() : program; >x : Program ->isBuilderProgram(program) ? program.getProgram() : program : Program | T +>isBuilderProgram(program) ? program.getProgram() : program : Program >isBuilderProgram(program) : boolean >isBuilderProgram : (program: Program | T) => program is T >program : Program | T @@ -234,5 +234,5 @@ export function listFiles(program: Program | T) { >program.getProgram : () => Program >program : T >getProgram : () => Program ->program : Program | T +>program : Program } diff --git a/tests/cases/compiler/genericCapturingFunctionNarrowing.ts b/tests/cases/compiler/genericCapturingFunctionNarrowing.ts new file mode 100644 index 0000000000000..bd6b86608c8cf --- /dev/null +++ b/tests/cases/compiler/genericCapturingFunctionNarrowing.ts @@ -0,0 +1,13 @@ +function needsToNarrowTheType(thing: First | Second) { + if (hasAFoo(thing)) { + console.log(thing.foo); + } + else { + // I would expect this to work because the type should be narrowed in this branch to `Second` + console.log(thing.bar); // Error: Property 'bar' does not exist on type 'First | Second'. + } + + function hasAFoo(value: First | Second): value is First { + return "foo" in value; + } +} \ No newline at end of file