Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch statement over a union of literals is not exhaustive when checking against enum members #53736

Open
boconnell opened this issue Apr 11, 2023 · 1 comment · May be fixed by #53751
Open
Labels
Experience Enhancement Noncontroversial enhancements Help Wanted You can do this Suggestion An idea for TypeScript
Milestone

Comments

@boconnell
Copy link

boconnell commented Apr 11, 2023

Bug Report

The following switch-case is unexpectedly not exhaustive:

enum X {
    FOO = "foo",
    BAR = "bar"
}

function test(x: "foo" | "bar") {
    switch (x) {
        case X.FOO:
            return x;
        case X.BAR:
            return x;
    }
}

Because X.FOO and X.BAR are just strings under the hood, I would expect this to compile as exhaustive. It's doubly surprising because:

  • Typescript correctly narrows the type of x within each case branch
  • this works if I check directly against the string literals or a const object (e.g. const XConst = { FOO: "foo", BAR: "bar" } as const)

🔎 Search Terms

switch union literals exhaustive enum

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about enum, literals, switch statements, type narrowing

⏯ Playground Link

Playground link with relevant code

💻 Code

enum X {
    FOO = "foo",
    BAR = "bar"
}

const XConst = {
    FOO: "foo",
    BAR: "bar"
} as const;

// does not compile
function test(x: "foo" | "bar") {
    switch (x) {
        case X.FOO:
            return x;
        case X.BAR:
            return x;
    }
}

// compiles
function test2(x: X) {
    switch (x) {
        case X.FOO:
            return x;
        case X.BAR:
            return x;
    }
}

// compiles
function test3(x: "foo" | "bar") {
    switch (x) {
        case "foo":
            return x;
        case "bar":
            return x;
    }
}

// compiles
function test4(x: "foo" | "bar") {
    switch (x) {
        case XConst.FOO:
            return x;
        case XConst.BAR:
            return x;
    }
}

🙁 Actual behavior

function test does not compile

🙂 Expected behavior

Function test compiles

@fatcerberus
Copy link

In general enum members are not considered interchangeable with their literal values by the type system; I’m surprised this even compiles.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Experience Enhancement Noncontroversial enhancements labels Apr 11, 2023
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Apr 11, 2023
@RyanCavanaugh RyanCavanaugh added the Help Wanted You can do this label Apr 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Experience Enhancement Noncontroversial enhancements Help Wanted You can do this Suggestion An idea for TypeScript
Projects
None yet
3 participants