Skip to content

Commit b4e02ab

Browse files
committed
fix(50340): narrow type by discriminant in typeof
1 parent 15f7b6f commit b4e02ab

File tree

5 files changed

+74
-0
lines changed

5 files changed

+74
-0
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25260,11 +25260,19 @@ namespace ts {
2526025260
}
2526125261
const target = getReferenceCandidate(typeOfExpr.expression);
2526225262
if (!isMatchingReference(reference, target)) {
25263+
const propertyAccess = getDiscriminantPropertyAccess(typeOfExpr.expression, type);
25264+
if (propertyAccess) {
25265+
return narrowTypeByDiscriminant(type, propertyAccess, t => narrowTypeByLiteralExpression(t, literal, assumeTrue));
25266+
}
2526325267
if (strictNullChecks && optionalChainContainsReference(target, reference) && assumeTrue === (literal.text !== "undefined")) {
2526425268
return getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
2526525269
}
2526625270
return type;
2526725271
}
25272+
return narrowTypeByLiteralExpression(type, literal, assumeTrue);
25273+
}
25274+
25275+
function narrowTypeByLiteralExpression(type: Type, literal: LiteralExpression, assumeTrue: boolean) {
2526825276
return assumeTrue ?
2526925277
narrowTypeByTypeName(type, literal.text) :
2527025278
getTypeWithFacts(type, typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//// [narrowingTypeofUndefined.ts]
2+
declare const a: { error: { a: string }, result: undefined } | { error: undefined, result: { b: number } }
3+
4+
if (typeof a.error === 'undefined') {
5+
a.result.b; // ok
6+
}
7+
8+
9+
//// [narrowingTypeofUndefined.js]
10+
if (typeof a.error === 'undefined') {
11+
a.result.b; // ok
12+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/narrowingTypeofUndefined.ts ===
2+
declare const a: { error: { a: string }, result: undefined } | { error: undefined, result: { b: number } }
3+
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
4+
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18))
5+
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 27))
6+
>result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 40))
7+
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 64))
8+
>result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 82))
9+
>b : Symbol(b, Decl(narrowingTypeofUndefined.ts, 0, 92))
10+
11+
if (typeof a.error === 'undefined') {
12+
>a.error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 64))
13+
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
14+
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 64))
15+
16+
a.result.b; // ok
17+
>a.result.b : Symbol(b, Decl(narrowingTypeofUndefined.ts, 0, 92))
18+
>a.result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 40), Decl(narrowingTypeofUndefined.ts, 0, 82))
19+
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
20+
>result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 40), Decl(narrowingTypeofUndefined.ts, 0, 82))
21+
>b : Symbol(b, Decl(narrowingTypeofUndefined.ts, 0, 92))
22+
}
23+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/compiler/narrowingTypeofUndefined.ts ===
2+
declare const a: { error: { a: string }, result: undefined } | { error: undefined, result: { b: number } }
3+
>a : { error: { a: string;}; result: undefined; } | { error: undefined; result: { b: number;}; }
4+
>error : { a: string; }
5+
>a : string
6+
>result : undefined
7+
>error : undefined
8+
>result : { b: number; }
9+
>b : number
10+
11+
if (typeof a.error === 'undefined') {
12+
>typeof a.error === 'undefined' : boolean
13+
>typeof a.error : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
14+
>a.error : { a: string; }
15+
>a : { error: { a: string; }; result: undefined; } | { error: undefined; result: { b: number; }; }
16+
>error : { a: string; }
17+
>'undefined' : "undefined"
18+
19+
a.result.b; // ok
20+
>a.result.b : number
21+
>a.result : { b: number; }
22+
>a : { error: { a: string; }; result: undefined; } | { error: undefined; result: { b: number; }; }
23+
>result : { b: number; }
24+
>b : number
25+
}
26+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
declare const a: { error: { a: string }, result: undefined } | { error: undefined, result: { b: number } }
2+
3+
if (typeof a.error === 'undefined') {
4+
a.result.b; // ok
5+
}

0 commit comments

Comments
 (0)