Skip to content

typeof ... === "undefined" check on discriminated union of undefined and object type doesn't narrow correctly #50340

Closed
@fmmoret

Description

@fmmoret

Bug Report

🔎 Search Terms

typeof narrow, typeof discriminated union

🕗 Version & Regression Information

Affects all playground versions.

Please keep and fill in the line that best applies:
-->

The FAQ entry is TODO, but I don't think it applies here, the union is not & 'd with the type guard type at all.

⏯ Playground Link

Playground link with relevant code

💻 Code

https://www.typescriptlang.org/play?ts=4.8.0-beta#code/MYewdgzgLgBAhjAvDA3gX3hVBTATrkXALlThOlwEswBzNAGhl2wgFcAbKE1sAE2wBm1bLwwAfVDDwFiPfkLAjGzNpxIoARiTCsAthrxo0AKHbZYWmBWo0A3KfMxgRHfrz3KAgBRQAngAdsEAF4ADppQhgAQkRkAHI5QWFeOIBKVGMYLJgNJDCI3FC4WxgAelKYMDh8EAB3EWMMbHYIbAzspzy4UJUOKFCNEvLKkFgqmvreRuNPL26C6NiYRIURdJRM7NzkeZrC4rKK8YJJxqkWto2O4C6elj6BoaPqk4a0IA

const a = {} as {error: {a: string}, result: undefined} | { error:undefined, result: {b: number}}
let b: string;
let c:number;
if(typeof a.error !== 'undefined') {
    b = a.error.a; // narrowed
} else {
    c = a.result.b; // not narrowed
}
if(a.error !== undefined) {
    b = a.error.a; // narrowed
} else {
    c = a.result.b; // narrowed
}

A similar example that I also expected to work but actually fails for both cases:
https://www.typescriptlang.org/play?ts=4.8.0-beta#code/MYewdgzgLgBAhjAvDA3gX3hVBTATrkXALlThOlwEswBzNAGhl2wgFcAbKEsVgWwCM8MDAB9UMPAWIweAvI2ZtOJFP3JQqtNGgBQ7bLDUwK1GgG49BmMCKzBuC-tiUIAOT72kMABQAPEqxgANZgIADuYACUJL4wLjIeQogAfDBQAJ4ADtggAGYwsYhFMADkdnglFpS53hnZefAAdJKEMACExWWJuCWRqDowgzD8XnDN+ISNcGYwAPSzMnATYdgAJjoY2OwQ2P1D1qONihxQjfwz8zIgsGBLBCvrutXebS7ucrjeYy24kX0oAyGI2Q3wmuCmFwWt2Waw2Em2uwB+2Ah2OnDOkKuNzu4VhaCAA

const a = {} as {error: {a: string}, result: number } | { error: number, result: {b: string}}
let b: string;
let c:number;
let isNumber = (x: unknown): x is number => typeof x === 'number';
if(typeof a.error !== 'number') {
    b = a.error.a; // narrowed
} else {
    c = a.result.b; // not narrowed
}
if(!isNumber(a.error)) {
    b = a.error.a; // narrowed
} else {
    c = a.result.b; // not narrowed
}

🙁 Actual behavior

typeof based narrowing only narrowed one code flow branch.

🙂 Expected behavior

typeof based narrowing should narrow both sides of the branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions