Skip to content

Commit 3c0a3e5

Browse files
authored
fix(52440): typeof ... === "undefined" check on mapped object member doesn't narrow correctly, for only some purposes (#52456)
1 parent 39a9ac8 commit 3c0a3e5

10 files changed

+174
-56
lines changed

src/compiler/checker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26839,7 +26839,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2683926839
function narrowTypeByLiteralExpression(type: Type, literal: LiteralExpression, assumeTrue: boolean) {
2684026840
return assumeTrue ?
2684126841
narrowTypeByTypeName(type, literal.text) :
26842-
getTypeWithFacts(type, typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject);
26842+
getAdjustedTypeWithFacts(type, typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject);
2684326843
}
2684426844

2684526845
function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) {

tests/baselines/reference/narrowingTypeofUndefined.symbols

-52
This file was deleted.

tests/baselines/reference/narrowingTypeofUndefined.js renamed to tests/baselines/reference/narrowingTypeofUndefined1.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//// [narrowingTypeofUndefined.ts]
1+
//// [narrowingTypeofUndefined1.ts]
22
declare const a: { error: { prop: string }, result: undefined } | { error: undefined, result: { prop: number } }
33

44
if (typeof a.error === 'undefined') {
@@ -16,7 +16,7 @@ else {
1616
}
1717

1818

19-
//// [narrowingTypeofUndefined.js]
19+
//// [narrowingTypeofUndefined1.js]
2020
if (typeof a.error === 'undefined') {
2121
a.result.prop; // number
2222
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/compiler/narrowingTypeofUndefined1.ts ===
2+
declare const a: { error: { prop: string }, result: undefined } | { error: undefined, result: { prop: number } }
3+
>a : Symbol(a, Decl(narrowingTypeofUndefined1.ts, 0, 13))
4+
>error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18))
5+
>prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 27))
6+
>result : Symbol(result, Decl(narrowingTypeofUndefined1.ts, 0, 43))
7+
>error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 67))
8+
>result : Symbol(result, Decl(narrowingTypeofUndefined1.ts, 0, 85))
9+
>prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 95))
10+
11+
if (typeof a.error === 'undefined') {
12+
>a.error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
13+
>a : Symbol(a, Decl(narrowingTypeofUndefined1.ts, 0, 13))
14+
>error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
15+
16+
a.result.prop; // number
17+
>a.result.prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 95))
18+
>a.result : Symbol(result, Decl(narrowingTypeofUndefined1.ts, 0, 43), Decl(narrowingTypeofUndefined1.ts, 0, 85))
19+
>a : Symbol(a, Decl(narrowingTypeofUndefined1.ts, 0, 13))
20+
>result : Symbol(result, Decl(narrowingTypeofUndefined1.ts, 0, 43), Decl(narrowingTypeofUndefined1.ts, 0, 85))
21+
>prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 95))
22+
}
23+
else {
24+
a.error.prop; // string
25+
>a.error.prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 27))
26+
>a.error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
27+
>a : Symbol(a, Decl(narrowingTypeofUndefined1.ts, 0, 13))
28+
>error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
29+
>prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 27))
30+
}
31+
32+
if (typeof a.error !== 'undefined') {
33+
>a.error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
34+
>a : Symbol(a, Decl(narrowingTypeofUndefined1.ts, 0, 13))
35+
>error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
36+
37+
a.error.prop; // string
38+
>a.error.prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 27))
39+
>a.error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
40+
>a : Symbol(a, Decl(narrowingTypeofUndefined1.ts, 0, 13))
41+
>error : Symbol(error, Decl(narrowingTypeofUndefined1.ts, 0, 18), Decl(narrowingTypeofUndefined1.ts, 0, 67))
42+
>prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 27))
43+
}
44+
else {
45+
a.result.prop; // number
46+
>a.result.prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 95))
47+
>a.result : Symbol(result, Decl(narrowingTypeofUndefined1.ts, 0, 43), Decl(narrowingTypeofUndefined1.ts, 0, 85))
48+
>a : Symbol(a, Decl(narrowingTypeofUndefined1.ts, 0, 13))
49+
>result : Symbol(result, Decl(narrowingTypeofUndefined1.ts, 0, 43), Decl(narrowingTypeofUndefined1.ts, 0, 85))
50+
>prop : Symbol(prop, Decl(narrowingTypeofUndefined1.ts, 0, 95))
51+
}
52+

tests/baselines/reference/narrowingTypeofUndefined.types renamed to tests/baselines/reference/narrowingTypeofUndefined1.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
=== tests/cases/compiler/narrowingTypeofUndefined.ts ===
1+
=== tests/cases/compiler/narrowingTypeofUndefined1.ts ===
22
declare const a: { error: { prop: string }, result: undefined } | { error: undefined, result: { prop: number } }
33
>a : { error: { prop: string;}; result: undefined; } | { error: undefined; result: { prop: number;}; }
44
>error : { prop: string; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [narrowingTypeofUndefined2.ts]
2+
declare function takeArray(arr: Array<unknown>): void;
3+
4+
function fn<T extends Array<unknown> | undefined>(arg: T) {
5+
if (typeof arg !== "undefined") {
6+
takeArray(arg);
7+
const n: Array<unknown> = arg;
8+
9+
for (const p of arg) { }
10+
const m = [...arg];
11+
}
12+
}
13+
14+
15+
//// [narrowingTypeofUndefined2.js]
16+
"use strict";
17+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
18+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
19+
if (ar || !(i in from)) {
20+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
21+
ar[i] = from[i];
22+
}
23+
}
24+
return to.concat(ar || Array.prototype.slice.call(from));
25+
};
26+
function fn(arg) {
27+
if (typeof arg !== "undefined") {
28+
takeArray(arg);
29+
var n = arg;
30+
for (var _i = 0, arg_1 = arg; _i < arg_1.length; _i++) {
31+
var p = arg_1[_i];
32+
}
33+
var m = __spreadArray([], arg, true);
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/narrowingTypeofUndefined2.ts ===
2+
declare function takeArray(arr: Array<unknown>): void;
3+
>takeArray : Symbol(takeArray, Decl(narrowingTypeofUndefined2.ts, 0, 0))
4+
>arr : Symbol(arr, Decl(narrowingTypeofUndefined2.ts, 0, 27))
5+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
6+
7+
function fn<T extends Array<unknown> | undefined>(arg: T) {
8+
>fn : Symbol(fn, Decl(narrowingTypeofUndefined2.ts, 0, 54))
9+
>T : Symbol(T, Decl(narrowingTypeofUndefined2.ts, 2, 12))
10+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
11+
>arg : Symbol(arg, Decl(narrowingTypeofUndefined2.ts, 2, 50))
12+
>T : Symbol(T, Decl(narrowingTypeofUndefined2.ts, 2, 12))
13+
14+
if (typeof arg !== "undefined") {
15+
>arg : Symbol(arg, Decl(narrowingTypeofUndefined2.ts, 2, 50))
16+
17+
takeArray(arg);
18+
>takeArray : Symbol(takeArray, Decl(narrowingTypeofUndefined2.ts, 0, 0))
19+
>arg : Symbol(arg, Decl(narrowingTypeofUndefined2.ts, 2, 50))
20+
21+
const n: Array<unknown> = arg;
22+
>n : Symbol(n, Decl(narrowingTypeofUndefined2.ts, 5, 13))
23+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
24+
>arg : Symbol(arg, Decl(narrowingTypeofUndefined2.ts, 2, 50))
25+
26+
for (const p of arg) { }
27+
>p : Symbol(p, Decl(narrowingTypeofUndefined2.ts, 7, 18))
28+
>arg : Symbol(arg, Decl(narrowingTypeofUndefined2.ts, 2, 50))
29+
30+
const m = [...arg];
31+
>m : Symbol(m, Decl(narrowingTypeofUndefined2.ts, 8, 13))
32+
>arg : Symbol(arg, Decl(narrowingTypeofUndefined2.ts, 2, 50))
33+
}
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/compiler/narrowingTypeofUndefined2.ts ===
2+
declare function takeArray(arr: Array<unknown>): void;
3+
>takeArray : (arr: Array<unknown>) => void
4+
>arr : unknown[]
5+
6+
function fn<T extends Array<unknown> | undefined>(arg: T) {
7+
>fn : <T extends unknown[] | undefined>(arg: T) => void
8+
>arg : T
9+
10+
if (typeof arg !== "undefined") {
11+
>typeof arg !== "undefined" : boolean
12+
>typeof arg : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
13+
>arg : T
14+
>"undefined" : "undefined"
15+
16+
takeArray(arg);
17+
>takeArray(arg) : void
18+
>takeArray : (arr: unknown[]) => void
19+
>arg : unknown[]
20+
21+
const n: Array<unknown> = arg;
22+
>n : unknown[]
23+
>arg : unknown[]
24+
25+
for (const p of arg) { }
26+
>p : unknown
27+
>arg : T & {}
28+
29+
const m = [...arg];
30+
>m : (T & {})[number][]
31+
>[...arg] : (T & {})[number][]
32+
>...arg : unknown
33+
>arg : T & {}
34+
}
35+
}
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @strict: true
2+
declare function takeArray(arr: Array<unknown>): void;
3+
4+
function fn<T extends Array<unknown> | undefined>(arg: T) {
5+
if (typeof arg !== "undefined") {
6+
takeArray(arg);
7+
const n: Array<unknown> = arg;
8+
9+
for (const p of arg) { }
10+
const m = [...arg];
11+
}
12+
}

0 commit comments

Comments
 (0)