diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9a71a6a0277e8..f7c1c34c81c36 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22165,7 +22165,8 @@ namespace ts { } function narrowByInKeyword(type: Type, literal: LiteralExpression, assumeTrue: boolean) { - if (type.flags & (TypeFlags.Union | TypeFlags.Object) + if (type.flags & TypeFlags.Union + || type.flags & TypeFlags.Object && declaredType !== type || isThisTypeParameter(type) || type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => t.symbol !== globalThisSymbol)) { const propName = escapeLeadingUnderscores(literal.text); diff --git a/tests/baselines/reference/fixSignatureCaching.errors.txt b/tests/baselines/reference/fixSignatureCaching.errors.txt index 4bb2276c3c554..d1c3ce6394fa8 100644 --- a/tests/baselines/reference/fixSignatureCaching.errors.txt +++ b/tests/baselines/reference/fixSignatureCaching.errors.txt @@ -3,7 +3,6 @@ tests/cases/conformance/fixSignatureCaching.ts(284,10): error TS2339: Property ' tests/cases/conformance/fixSignatureCaching.ts(293,10): error TS2339: Property 'FALLBACK_PHONE' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(294,10): error TS2339: Property 'FALLBACK_TABLET' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(295,10): error TS2339: Property 'FALLBACK_MOBILE' does not exist on type '{}'. -tests/cases/conformance/fixSignatureCaching.ts(301,17): error TS2339: Property 'isArray' does not exist on type 'never'. tests/cases/conformance/fixSignatureCaching.ts(330,74): error TS2339: Property 'mobileDetectRules' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(369,10): error TS2339: Property 'findMatch' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(387,10): error TS2339: Property 'findMatches' does not exist on type '{}'. @@ -59,7 +58,7 @@ tests/cases/conformance/fixSignatureCaching.ts(981,16): error TS2304: Cannot fin tests/cases/conformance/fixSignatureCaching.ts(983,44): error TS2339: Property 'MobileDetect' does not exist on type 'Window & typeof globalThis'. -==== tests/cases/conformance/fixSignatureCaching.ts (59 errors) ==== +==== tests/cases/conformance/fixSignatureCaching.ts (58 errors) ==== // Repro from #10697 (function (define, undefined) { @@ -371,8 +370,6 @@ tests/cases/conformance/fixSignatureCaching.ts(983,44): error TS2339: Property ' isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray; - ~~~~~~~ -!!! error TS2339: Property 'isArray' does not exist on type 'never'. function equalIC(a, b) { return a != null && b != null && a.toLowerCase() === b.toLowerCase(); diff --git a/tests/baselines/reference/fixSignatureCaching.symbols b/tests/baselines/reference/fixSignatureCaching.symbols index 97efe41ce1205..0ec1d2c63aae8 100644 --- a/tests/baselines/reference/fixSignatureCaching.symbols +++ b/tests/baselines/reference/fixSignatureCaching.symbols @@ -825,7 +825,9 @@ define(function () { >value : Symbol(value, Decl(fixSignatureCaching.ts, 299, 20)) : Array.isArray; +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) >Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) function equalIC(a, b) { >equalIC : Symbol(equalIC, Decl(fixSignatureCaching.ts, 300, 24)) diff --git a/tests/baselines/reference/fixSignatureCaching.types b/tests/baselines/reference/fixSignatureCaching.types index 8765e8fc310f4..7168cdf9fe238 100644 --- a/tests/baselines/reference/fixSignatureCaching.types +++ b/tests/baselines/reference/fixSignatureCaching.types @@ -1127,9 +1127,9 @@ define(function () { >'[object Array]' : "[object Array]" isArray = 'isArray' in Array ->isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : any +>isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : (value: any) => boolean >isArray : any ->'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : any +>'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : (value: any) => boolean >'isArray' in Array : boolean >'isArray' : "isArray" >Array : ArrayConstructor @@ -1150,9 +1150,9 @@ define(function () { >'[object Array]' : "[object Array]" : Array.isArray; ->Array.isArray : any ->Array : never ->isArray : any +>Array.isArray : (arg: {} | T) => arg is T extends readonly any[] ? unknown extends T ? never : readonly any[] : any[] +>Array : ArrayConstructor +>isArray : (arg: {} | T) => arg is T extends readonly any[] ? unknown extends T ? never : readonly any[] : any[] function equalIC(a, b) { >equalIC : (a: any, b: any) => boolean diff --git a/tests/baselines/reference/inKeywordTypeguard.errors.txt b/tests/baselines/reference/inKeywordTypeguard.errors.txt index bb7d823d99d43..cfb2b090a7af5 100644 --- a/tests/baselines/reference/inKeywordTypeguard.errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard.errors.txt @@ -165,6 +165,46 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do let n: never = x; } } + + // Repro from #38608 + declare const error: Error; + if ('extra' in error) { + error // Still Error + } else { + error // Error + } + + function narrowsToNever(x: { l: number } | { r: number }) { + let v: number; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x + } + return v; + } + + type AOrB = { aProp: number } | { bProp: number }; + declare function isAOrB(x: unknown): x is AOrB; + + declare var x: unknown; + if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never: never = x; + } + } + function negativeIntersectionTest() { if ("ontouchstart" in window) { window.ontouchstart diff --git a/tests/baselines/reference/inKeywordTypeguard.js b/tests/baselines/reference/inKeywordTypeguard.js index d5a11599b64bb..e91a6ddf6e4d4 100644 --- a/tests/baselines/reference/inKeywordTypeguard.js +++ b/tests/baselines/reference/inKeywordTypeguard.js @@ -104,6 +104,46 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { let n: never = x; } } + +// Repro from #38608 +declare const error: Error; +if ('extra' in error) { + error // Still Error +} else { + error // Error +} + +function narrowsToNever(x: { l: number } | { r: number }) { + let v: number; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x + } + return v; +} + +type AOrB = { aProp: number } | { bProp: number }; +declare function isAOrB(x: unknown): x is AOrB; + +declare var x: unknown; +if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never: never = x; + } +} + function negativeIntersectionTest() { if ("ontouchstart" in window) { window.ontouchstart @@ -252,6 +292,37 @@ function positiveIntersectionTest(x) { var n = x; } } +if ('extra' in error) { + error; // Still Error +} +else { + error; // Error +} +function narrowsToNever(x) { + var v; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x; + } + return v; +} +if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + var _never = x; + } +} function negativeIntersectionTest() { if ("ontouchstart" in window) { window.ontouchstart; diff --git a/tests/baselines/reference/inKeywordTypeguard.symbols b/tests/baselines/reference/inKeywordTypeguard.symbols index 5ef75cbe25544..91d9c42597740 100644 --- a/tests/baselines/reference/inKeywordTypeguard.symbols +++ b/tests/baselines/reference/inKeywordTypeguard.symbols @@ -261,8 +261,105 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { >x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34)) } } + +// Repro from #38608 +declare const error: Error; +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +if ('extra' in error) { +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) + + error // Still Error +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) + +} else { + error // Error +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) +} + +function narrowsToNever(x: { l: number } | { r: number }) { +>narrowsToNever : Symbol(narrowsToNever, Decl(inKeywordTypeguard.ts, 112, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) +>l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28)) +>r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44)) + + let v: number; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) + + if ("l" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) + + v = x.l; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +>x.l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) +>l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28)) + } + else if ("r" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) + + v = x.r; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +>x.r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) +>r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44)) + } + else { + v = x +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) + } + return v; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +} + +type AOrB = { aProp: number } | { bProp: number }; +>AOrB : Symbol(AOrB, Decl(inKeywordTypeguard.ts, 126, 1)) +>aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13)) +>bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33)) + +declare function isAOrB(x: unknown): x is AOrB; +>isAOrB : Symbol(isAOrB, Decl(inKeywordTypeguard.ts, 128, 50)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 129, 24)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 129, 24)) +>AOrB : Symbol(AOrB, Decl(inKeywordTypeguard.ts, 126, 1)) + +declare var x: unknown; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + +if (isAOrB(x)) { +>isAOrB : Symbol(isAOrB, Decl(inKeywordTypeguard.ts, 128, 50)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + if ("aProp" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + x.aProp; +>x.aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) +>aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13)) + } + else if ("bProp" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + x.bProp; +>x.bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) +>bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33)) + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + const _never: never = x; +>_never : Symbol(_never, Decl(inKeywordTypeguard.ts, 141, 13)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + } +} + function negativeIntersectionTest() { ->negativeIntersectionTest : Symbol(negativeIntersectionTest, Decl(inKeywordTypeguard.ts, 104, 1)) +>negativeIntersectionTest : Symbol(negativeIntersectionTest, Decl(inKeywordTypeguard.ts, 143, 1)) if ("ontouchstart" in window) { >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) diff --git a/tests/baselines/reference/inKeywordTypeguard.types b/tests/baselines/reference/inKeywordTypeguard.types index c5b735a51d9c1..e0956d838e2ec 100644 --- a/tests/baselines/reference/inKeywordTypeguard.types +++ b/tests/baselines/reference/inKeywordTypeguard.types @@ -321,6 +321,116 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { >x : never } } + +// Repro from #38608 +declare const error: Error; +>error : Error + +if ('extra' in error) { +>'extra' in error : boolean +>'extra' : "extra" +>error : Error + + error // Still Error +>error : Error + +} else { + error // Error +>error : Error +} + +function narrowsToNever(x: { l: number } | { r: number }) { +>narrowsToNever : (x: { l: number;} | { r: number;}) => number +>x : { l: number; } | { r: number; } +>l : number +>r : number + + let v: number; +>v : number + + if ("l" in x) { +>"l" in x : boolean +>"l" : "l" +>x : { l: number; } | { r: number; } + + v = x.l; +>v = x.l : number +>v : number +>x.l : number +>x : { l: number; } +>l : number + } + else if ("r" in x) { +>"r" in x : boolean +>"r" : "r" +>x : { r: number; } + + v = x.r; +>v = x.r : number +>v : number +>x.r : number +>x : { r: number; } +>r : number + } + else { + v = x +>v = x : never +>v : number +>x : never + } + return v; +>v : number +} + +type AOrB = { aProp: number } | { bProp: number }; +>AOrB : AOrB +>aProp : number +>bProp : number + +declare function isAOrB(x: unknown): x is AOrB; +>isAOrB : (x: unknown) => x is AOrB +>x : unknown + +declare var x: unknown; +>x : unknown + +if (isAOrB(x)) { +>isAOrB(x) : boolean +>isAOrB : (x: unknown) => x is AOrB +>x : unknown + + if ("aProp" in x) { +>"aProp" in x : boolean +>"aProp" : "aProp" +>x : AOrB + + x.aProp; +>x.aProp : number +>x : { aProp: number; } +>aProp : number + } + else if ("bProp" in x) { +>"bProp" in x : boolean +>"bProp" : "bProp" +>x : { bProp: number; } + + x.bProp; +>x.bProp : number +>x : { bProp: number; } +>bProp : number + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { +>"cProp" in x : boolean +>"cProp" : "cProp" +>x : never + + const _never: never = x; +>_never : never +>x : never + } +} + function negativeIntersectionTest() { >negativeIntersectionTest : () => void diff --git a/tests/cases/compiler/inKeywordTypeguard.ts b/tests/cases/compiler/inKeywordTypeguard.ts index d20404c949a86..e853d1935bee7 100644 --- a/tests/cases/compiler/inKeywordTypeguard.ts +++ b/tests/cases/compiler/inKeywordTypeguard.ts @@ -103,6 +103,46 @@ function positiveIntersectionTest(x: { a: string } & { b: string }) { let n: never = x; } } + +// Repro from #38608 +declare const error: Error; +if ('extra' in error) { + error // Still Error +} else { + error // Error +} + +function narrowsToNever(x: { l: number } | { r: number }) { + let v: number; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x + } + return v; +} + +type AOrB = { aProp: number } | { bProp: number }; +declare function isAOrB(x: unknown): x is AOrB; + +declare var x: unknown; +if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never: never = x; + } +} + function negativeIntersectionTest() { if ("ontouchstart" in window) { window.ontouchstart