Skip to content

Narrowing union types requires at least 2 membersΒ #63645

Description

πŸ”Ž Search Terms

narrowing, discriminated, union, member, count

πŸ•— Version & Regression Information

Tested on 3.3.3333, 5.9.3, 6.0.2, and 7.0.2, all exhibit the problem.

⏯ Playground Link

https://www.typescriptlang.org/play/?allowUnreachableCode=true#code/CYUwxgNghgTiAEYD2A7AzgF3gMyUgjAFzwDe8GxADANzwAexKArgLYBGIMtAno6x13gBfagCg0AdwCWGMAAt4AClwEAdBgCUpUfF2IoaBJWIY5MJBPgAiK2KGjk6LAH0pLAA5I0aKWwggieBQQADdOeABeHDx8WgB6OPgmFAMfAHMUvxBRHNBIWARHTGikACZiMgp4Gno+dk4eOoFaIXgAH1JyYlj4AC9iTBgpFDSWsUkZeSUVUvUtEh09MAMjEzMLa1tF3WXDeEDTc0sbOwdUYtcPLx8s8qDQ8KiZ+MSUDZA6OSgmTCkw+DQSBYIDkFly4GgcEQ5ywKgAzMQrFA2GAthNZAplHg4fNtvo9kiUVY1kdNqcii43J5vL5-Aj7mEYJESnCxKI8pDCjCSgAWRHI1HtaygbBo6QY6Z4Hm4vT4hCE1EkjYnPG7eUi4nkdbHLb2CnwS7Um7+PkMx68tkcgrQpwlACs3Ro4nFUyxSDtMqWK32xi1pJVeu5huutJADrNTKeeDtloh1v1KgAbI6haUnejXUnPTtvfhfYdlVtZWr4GmlTryUGqSGssmI8yk2IgA

πŸ’» Code

declare const foo1: { t: 0; x: number; y: number; };
switch (foo1.t) {
    case 0: throw "";
}
const _impossible1: never = foo1; // unassignable


declare const foo2: { t: 0; x: number; y: number; } | { t: 1; z: string; };
switch (foo2.t) {
    case 0: throw "";
    case 1: throw "";
}
const _impossible2: never = foo2; // now exhaustive somehow

This is a reduced example, but the idea is:

  • there is some discriminated union type and it was narrowed elsewhere, which happens to leave only a single variant left currently
  • there is a secondary narrowing, because the discriminated union is expected to get more members in the future while the first narrowing will remain unchanged
  • this rather surprisingly and unintuitively causes an error

Somewhat interestingly, this is not a problem with primitives:

declare const foo3: "abc";
switch (foo3) {
    case "abc": throw "";
}
const _impossible3: never = foo3;

declare const foo4: "abc" | "def";
switch (foo4) {
    case "abc": throw "";
    case "def": throw "";
}
const _impossible4: never = foo4;

declare const foo5: 10;
switch (foo5) {
    case 10: throw "";
}
const _impossible5: never = foo5;

declare const foo6: 10 | 20;
switch (foo6) {
    case 10: throw "";
    case 20: throw "";
}
const _impossible6: never = foo6;

πŸ™ Actual behavior

Discriminated unions rather surprisingly and unintuitively cannot be narrowed when it has only 1 member

πŸ™‚ Expected behavior

Discriminated unions should still be narrowable when it has only 1 member, to avoid surprises

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions