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

    No labels
    No labels

    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