mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
fix(50340): typeof ... === "undefined" check on discriminated union of undefined and object type doesn't narrow correctly (#50344)
* fix(50340): narrow type by discriminant in typeof * add additional test cases
This commit is contained in:
parent
43f8ae6df4
commit
a9797d218d
@ -25328,11 +25328,19 @@ namespace ts {
|
||||
}
|
||||
const target = getReferenceCandidate(typeOfExpr.expression);
|
||||
if (!isMatchingReference(reference, target)) {
|
||||
const propertyAccess = getDiscriminantPropertyAccess(typeOfExpr.expression, type);
|
||||
if (propertyAccess) {
|
||||
return narrowTypeByDiscriminant(type, propertyAccess, t => narrowTypeByLiteralExpression(t, literal, assumeTrue));
|
||||
}
|
||||
if (strictNullChecks && optionalChainContainsReference(target, reference) && assumeTrue === (literal.text !== "undefined")) {
|
||||
return getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
return narrowTypeByLiteralExpression(type, literal, assumeTrue);
|
||||
}
|
||||
|
||||
function narrowTypeByLiteralExpression(type: Type, literal: LiteralExpression, assumeTrue: boolean) {
|
||||
return assumeTrue ?
|
||||
narrowTypeByTypeName(type, literal.text) :
|
||||
getTypeWithFacts(type, typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject);
|
||||
|
||||
31
tests/baselines/reference/narrowingTypeofUndefined.js
Normal file
31
tests/baselines/reference/narrowingTypeofUndefined.js
Normal file
@ -0,0 +1,31 @@
|
||||
//// [narrowingTypeofUndefined.ts]
|
||||
declare const a: { error: { prop: string }, result: undefined } | { error: undefined, result: { prop: number } }
|
||||
|
||||
if (typeof a.error === 'undefined') {
|
||||
a.result.prop; // number
|
||||
}
|
||||
else {
|
||||
a.error.prop; // string
|
||||
}
|
||||
|
||||
if (typeof a.error !== 'undefined') {
|
||||
a.error.prop; // string
|
||||
}
|
||||
else {
|
||||
a.result.prop; // number
|
||||
}
|
||||
|
||||
|
||||
//// [narrowingTypeofUndefined.js]
|
||||
if (typeof a.error === 'undefined') {
|
||||
a.result.prop; // number
|
||||
}
|
||||
else {
|
||||
a.error.prop; // string
|
||||
}
|
||||
if (typeof a.error !== 'undefined') {
|
||||
a.error.prop; // string
|
||||
}
|
||||
else {
|
||||
a.result.prop; // number
|
||||
}
|
||||
52
tests/baselines/reference/narrowingTypeofUndefined.symbols
Normal file
52
tests/baselines/reference/narrowingTypeofUndefined.symbols
Normal file
@ -0,0 +1,52 @@
|
||||
=== tests/cases/compiler/narrowingTypeofUndefined.ts ===
|
||||
declare const a: { error: { prop: string }, result: undefined } | { error: undefined, result: { prop: number } }
|
||||
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
|
||||
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18))
|
||||
>prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 27))
|
||||
>result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 43))
|
||||
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
>result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 85))
|
||||
>prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 95))
|
||||
|
||||
if (typeof a.error === 'undefined') {
|
||||
>a.error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
|
||||
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
|
||||
a.result.prop; // number
|
||||
>a.result.prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 95))
|
||||
>a.result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 43), Decl(narrowingTypeofUndefined.ts, 0, 85))
|
||||
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
|
||||
>result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 43), Decl(narrowingTypeofUndefined.ts, 0, 85))
|
||||
>prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 95))
|
||||
}
|
||||
else {
|
||||
a.error.prop; // string
|
||||
>a.error.prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 27))
|
||||
>a.error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
|
||||
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
>prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 27))
|
||||
}
|
||||
|
||||
if (typeof a.error !== 'undefined') {
|
||||
>a.error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
|
||||
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
|
||||
a.error.prop; // string
|
||||
>a.error.prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 27))
|
||||
>a.error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
|
||||
>error : Symbol(error, Decl(narrowingTypeofUndefined.ts, 0, 18), Decl(narrowingTypeofUndefined.ts, 0, 67))
|
||||
>prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 27))
|
||||
}
|
||||
else {
|
||||
a.result.prop; // number
|
||||
>a.result.prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 95))
|
||||
>a.result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 43), Decl(narrowingTypeofUndefined.ts, 0, 85))
|
||||
>a : Symbol(a, Decl(narrowingTypeofUndefined.ts, 0, 13))
|
||||
>result : Symbol(result, Decl(narrowingTypeofUndefined.ts, 0, 43), Decl(narrowingTypeofUndefined.ts, 0, 85))
|
||||
>prop : Symbol(prop, Decl(narrowingTypeofUndefined.ts, 0, 95))
|
||||
}
|
||||
|
||||
58
tests/baselines/reference/narrowingTypeofUndefined.types
Normal file
58
tests/baselines/reference/narrowingTypeofUndefined.types
Normal file
@ -0,0 +1,58 @@
|
||||
=== tests/cases/compiler/narrowingTypeofUndefined.ts ===
|
||||
declare const a: { error: { prop: string }, result: undefined } | { error: undefined, result: { prop: number } }
|
||||
>a : { error: { prop: string;}; result: undefined; } | { error: undefined; result: { prop: number;}; }
|
||||
>error : { prop: string; }
|
||||
>prop : string
|
||||
>result : undefined
|
||||
>error : undefined
|
||||
>result : { prop: number; }
|
||||
>prop : number
|
||||
|
||||
if (typeof a.error === 'undefined') {
|
||||
>typeof a.error === 'undefined' : boolean
|
||||
>typeof a.error : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>a.error : { prop: string; }
|
||||
>a : { error: { prop: string; }; result: undefined; } | { error: undefined; result: { prop: number; }; }
|
||||
>error : { prop: string; }
|
||||
>'undefined' : "undefined"
|
||||
|
||||
a.result.prop; // number
|
||||
>a.result.prop : number
|
||||
>a.result : { prop: number; }
|
||||
>a : { error: { prop: string; }; result: undefined; } | { error: undefined; result: { prop: number; }; }
|
||||
>result : { prop: number; }
|
||||
>prop : number
|
||||
}
|
||||
else {
|
||||
a.error.prop; // string
|
||||
>a.error.prop : string
|
||||
>a.error : { prop: string; }
|
||||
>a : { error: { prop: string; }; result: undefined; } | { error: undefined; result: { prop: number; }; }
|
||||
>error : { prop: string; }
|
||||
>prop : string
|
||||
}
|
||||
|
||||
if (typeof a.error !== 'undefined') {
|
||||
>typeof a.error !== 'undefined' : boolean
|
||||
>typeof a.error : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>a.error : { prop: string; }
|
||||
>a : { error: { prop: string; }; result: undefined; } | { error: undefined; result: { prop: number; }; }
|
||||
>error : { prop: string; }
|
||||
>'undefined' : "undefined"
|
||||
|
||||
a.error.prop; // string
|
||||
>a.error.prop : string
|
||||
>a.error : { prop: string; }
|
||||
>a : { error: { prop: string; }; result: undefined; } | { error: undefined; result: { prop: number; }; }
|
||||
>error : { prop: string; }
|
||||
>prop : string
|
||||
}
|
||||
else {
|
||||
a.result.prop; // number
|
||||
>a.result.prop : number
|
||||
>a.result : { prop: number; }
|
||||
>a : { error: { prop: string; }; result: undefined; } | { error: undefined; result: { prop: number; }; }
|
||||
>result : { prop: number; }
|
||||
>prop : number
|
||||
}
|
||||
|
||||
15
tests/cases/compiler/narrowingTypeofUndefined.ts
Normal file
15
tests/cases/compiler/narrowingTypeofUndefined.ts
Normal file
@ -0,0 +1,15 @@
|
||||
declare const a: { error: { prop: string }, result: undefined } | { error: undefined, result: { prop: number } }
|
||||
|
||||
if (typeof a.error === 'undefined') {
|
||||
a.result.prop; // number
|
||||
}
|
||||
else {
|
||||
a.error.prop; // string
|
||||
}
|
||||
|
||||
if (typeof a.error !== 'undefined') {
|
||||
a.error.prop; // string
|
||||
}
|
||||
else {
|
||||
a.result.prop; // number
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user