From 46561980dd8d9eaedbf7cbf67965809c9aba8bbf Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 15 Jul 2020 14:59:03 -0700 Subject: [PATCH 1/2] Redo in-narrowing for intersections Still need to carve out an exception for globalThis --- src/compiler/checker.ts | 2 +- tests/baselines/reference/inKeywordTypeguard.errors.txt | 5 +---- tests/baselines/reference/inKeywordTypeguard.types | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ef279c287581a..3fa39912292ee 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21430,7 +21430,7 @@ namespace ts { } function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { - if (type.flags & (TypeFlags.Union | TypeFlags.Object) || isThisTypeParameter(type)) { + if (type.flags & (TypeFlags.Union | TypeFlags.Object | TypeFlags.Intersection) || isThisTypeParameter(type)) { const propName = escapeLeadingUnderscores(literal.text); return filterType(type, t => isTypePresencePossible(t, propName, assumeTrue)); } diff --git a/tests/baselines/reference/inKeywordTypeguard.errors.txt b/tests/baselines/reference/inKeywordTypeguard.errors.txt index 5aa04b170426c..f5885acfc8835 100644 --- a/tests/baselines/reference/inKeywordTypeguard.errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard.errors.txt @@ -19,10 +19,9 @@ tests/cases/compiler/inKeywordTypeguard.ts(74,32): error TS2339: Property 'a' do tests/cases/compiler/inKeywordTypeguard.ts(82,39): error TS2339: Property 'b' does not exist on type 'A'. tests/cases/compiler/inKeywordTypeguard.ts(84,39): error TS2339: Property 'a' does not exist on type 'B'. tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' does not exist on type 'never'. -tests/cases/compiler/inKeywordTypeguard.ts(103,13): error TS2322: Type '{ a: string; } & { b: string; }' is not assignable to type 'never'. -==== tests/cases/compiler/inKeywordTypeguard.ts (18 errors) ==== +==== tests/cases/compiler/inKeywordTypeguard.ts (17 errors) ==== class A { a: string; } class B { b: string; } @@ -164,8 +163,6 @@ tests/cases/compiler/inKeywordTypeguard.ts(103,13): error TS2322: Type '{ a: str let s: string = x.a; } else { let n: never = x; - ~ -!!! error TS2322: Type '{ a: string; } & { b: string; }' is not assignable to type 'never'. } } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordTypeguard.types b/tests/baselines/reference/inKeywordTypeguard.types index b15ab47d454e2..bd77a8da45ede 100644 --- a/tests/baselines/reference/inKeywordTypeguard.types +++ b/tests/baselines/reference/inKeywordTypeguard.types @@ -318,7 +318,7 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { } else { let n: never = x; >n : never ->x : { a: string; } & { b: string; } +>x : never } } From 67dc93682d926ca857297591e839010ee00c20ec Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 17 Jul 2020 08:43:18 -0700 Subject: [PATCH 2/2] exempt globalThis from `in` narrowing --- src/compiler/checker.ts | 4 +++- .../reference/inKeywordTypeguard.errors.txt | 7 +++++++ .../baselines/reference/inKeywordTypeguard.js | 15 ++++++++++++++ .../reference/inKeywordTypeguard.symbols | 18 +++++++++++++++++ .../reference/inKeywordTypeguard.types | 20 +++++++++++++++++++ tests/cases/compiler/inKeywordTypeguard.ts | 7 +++++++ 6 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3fa39912292ee..5c2b2d8731ac9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21430,7 +21430,9 @@ namespace ts { } function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { - if (type.flags & (TypeFlags.Union | TypeFlags.Object | TypeFlags.Intersection) || isThisTypeParameter(type)) { + if (type.flags & (TypeFlags.Union | TypeFlags.Object) + || isThisTypeParameter(type) + || type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => t.symbol !== globalThisSymbol)) { const propName = escapeLeadingUnderscores(literal.text); return filterType(type, t => isTypePresencePossible(t, propName, assumeTrue)); } diff --git a/tests/baselines/reference/inKeywordTypeguard.errors.txt b/tests/baselines/reference/inKeywordTypeguard.errors.txt index f5885acfc8835..bb7d823d99d43 100644 --- a/tests/baselines/reference/inKeywordTypeguard.errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard.errors.txt @@ -165,4 +165,11 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do let n: never = x; } } + function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart + } else { + window.ontouchstart + } + } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordTypeguard.js b/tests/baselines/reference/inKeywordTypeguard.js index 83499d7f54e3d..d5a11599b64bb 100644 --- a/tests/baselines/reference/inKeywordTypeguard.js +++ b/tests/baselines/reference/inKeywordTypeguard.js @@ -104,6 +104,13 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { let n: never = x; } } +function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart + } else { + window.ontouchstart + } +} //// [inKeywordTypeguard.js] @@ -245,3 +252,11 @@ function positiveIntersectionTest(x) { var n = x; } } +function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart; + } + else { + window.ontouchstart; + } +} diff --git a/tests/baselines/reference/inKeywordTypeguard.symbols b/tests/baselines/reference/inKeywordTypeguard.symbols index ff79a4aa54efb..5ef75cbe25544 100644 --- a/tests/baselines/reference/inKeywordTypeguard.symbols +++ b/tests/baselines/reference/inKeywordTypeguard.symbols @@ -261,4 +261,22 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { >x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34)) } } +function negativeIntersectionTest() { +>negativeIntersectionTest : Symbol(negativeIntersectionTest, Decl(inKeywordTypeguard.ts, 104, 1)) + + if ("ontouchstart" in window) { +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + + window.ontouchstart +>window.ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + } else { + window.ontouchstart +>window.ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + } +} diff --git a/tests/baselines/reference/inKeywordTypeguard.types b/tests/baselines/reference/inKeywordTypeguard.types index bd77a8da45ede..c5b735a51d9c1 100644 --- a/tests/baselines/reference/inKeywordTypeguard.types +++ b/tests/baselines/reference/inKeywordTypeguard.types @@ -321,4 +321,24 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { >x : never } } +function negativeIntersectionTest() { +>negativeIntersectionTest : () => void + + if ("ontouchstart" in window) { +>"ontouchstart" in window : boolean +>"ontouchstart" : "ontouchstart" +>window : Window & typeof globalThis + + window.ontouchstart +>window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) +>window : Window & typeof globalThis +>ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) + + } else { + window.ontouchstart +>window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) +>window : Window & typeof globalThis +>ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) + } +} diff --git a/tests/cases/compiler/inKeywordTypeguard.ts b/tests/cases/compiler/inKeywordTypeguard.ts index 620aa401b252e..d20404c949a86 100644 --- a/tests/cases/compiler/inKeywordTypeguard.ts +++ b/tests/cases/compiler/inKeywordTypeguard.ts @@ -103,3 +103,10 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { let n: never = x; } } +function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart + } else { + window.ontouchstart + } +}