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-exhaustiveness-check] Require default case if discriminant type is not a union #2959

Closed
3 tasks done
raymondwang opened this issue Jan 21, 2021 · 6 comments · Fixed by #7880
Closed
3 tasks done
Labels
accepting prs Go ahead, send a pull request that resolves this issue enhancement: plugin rule option New rule option for an existing eslint-plugin rule package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin

Comments

@raymondwang
Copy link

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have read the FAQ and my problem is not listed.

Repro

{
  "rules": {
    "@typescript-eslint/switch-exhaustiveness-check": "warn"
  }
}
type Day = string; // or number

const day = 'Monday' as Day;
let result = 0;

switch (day) {
  case 'Monday': {
    result = 1;
    break;
  }
}

or

type Day = any; // or unknown, or a JS variable outside of a TS context

const day = 'Monday' as Day;
let result = 0;

switch (day) {
  case 'Monday': {
    result = 1;
    break;
  }
}

Expected Result

I would expect to see an error message compatible with the default-case ESLint rule, indicating that the switch-exhaustiveness-check rule is invalidated because the expression is not a union type.

Actual Result

No error message.

Additional Info

Essentially, I think the best of both worlds is to just have the predictable functionality of the default-case rule operate normally except in the event that a switch statement's expression is a union type. Today, there doesn't seem to be any way for the two rules to operate in tandem. Adding this functionality might be a breaking change, but I'm honestly a bit curious how controversial it would be.

@raymondwang raymondwang added package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for maintainers to take a look labels Jan 21, 2021
@bradzacher
Copy link
Member

bradzacher commented Jan 25, 2021

As it stands right now - this rule only checks union types.
So this is a large expansion in responsibilities for the rule.

<opinion>
I'll be honest when I say that I disagree that default-case is a "good" rule to have on.
There are cases where you know the constrained value set, but can't constrain the type (eg external library types), meaning you're working with a generic string or number.

So adding a default there just means you're going to add default: throw new Error("shouldn't happen");, which isn't a great practice by default.

Adding a default case also means that if you are able to convert the type to a union type in future, then you'll never catch any missing cases, as the rule would detect that all cases are handled.
</opinion>

I'm not sure if this is a good addition to this lint rule.
Maybe behind a default false flag? But that's, at best, what I'd be suggesting.

@bradzacher bradzacher added awaiting response Issues waiting for a reply from the OP or another party and removed triage Waiting for maintainers to take a look labels Jan 25, 2021
@raymondwang
Copy link
Author

raymondwang commented Jan 27, 2021

Thanks for the response @bradzacher. I think that's totally fair, and I don't want to overcomplicate this process or rule by enforcing my opinions with a breaking change. I basically just want to implement some solution for codebases that do want to enforce default-case as well as switch-exhaustiveness-check, which are totally incompatible rules today (despite both being in the interest of encouraging switch statement thoroughness).

Is there a way to recognize whether the default-case rule is being enforced within the rule? Could be an elegant solution, rather than passing in another config, but obviously happy to do the latter if not.

@bradzacher
Copy link
Member

Is there a way to recognize whether the default-case rule is being enforced within the rule?

No. Lint rules are all isolated. They have no way to tell if other rules are enabled.


Happy to have this behind a default off option!

@SimonSimCity
Copy link

SimonSimCity commented Jun 26, 2023

Just a work-around I've found which works for me:

If you are the type of guy who wants this feature, it might be worth thinking about an exhaustive matching guard (https://medium.com/technogise/type-safe-and-exhaustive-switch-statements-aka-pattern-matching-in-typescript-e3febd433a7a#be77). This way, you keep default-case active and use the fact that TypeScript has this special type never. When you have an exhaustive switch-case, let the compiler ensure the type inside default is never. Here's an example:

enum Direction {
  Up,
  Down,
  Left,
  Right,
}

(myVar: Direction) => {
  switch (myVar) {
    case Direction.Up:
      break;
    case Direction.Down:
      break;
    case Direction.Left:
      break;
    case Direction.Right:
      break;
    default:
      ((_: never) => { })(myVar)
  }
}

Playground

@ST-DDT
Copy link
Contributor

ST-DDT commented Oct 31, 2023

I'll have a look on this.

@ST-DDT
Copy link
Contributor

ST-DDT commented Nov 4, 2023

PR created: #7880

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 27, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepting prs Go ahead, send a pull request that resolves this issue enhancement: plugin rule option New rule option for an existing eslint-plugin rule package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin
Projects
None yet
5 participants