-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Fix exhaustiveness checking for single-member enums in switch statements #62900
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
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: RyanCavanaugh <[email protected]>
Co-authored-by: RyanCavanaugh <[email protected]>
|
The description links to wrong issue with Fixed comment. The correct issue is below it, but it was surprising to end up in an issue that was already closed and didn’t seem related. |
|
@typescript-bot test it |
|
Hey @RyanCavanaugh, the results of running the DT tests are ready. Everything looks the same! |
|
@RyanCavanaugh Here are the results of running the user tests with tsc comparing There were infrastructure failures potentially unrelated to your change:
Otherwise... Everything looks good! |
|
@RyanCavanaugh Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@RyanCavanaugh Here are the results of running the top 400 repos with tsc comparing Everything looks good! |
Exhaustiveness checking failed when an enum had exactly one member. The type would not narrow to
neverin the default case even when all cases were handled:Changes:
narrowTypeBySwitchOnDiscriminantinchecker.tsto narrow non-union types toneverwhen exhaustively handled in switch default caseImplementation:
After computing the default type, check if the input type is non-union, unit-like, and already handled in switch cases. Return
neverif conditions met. Applies only to switch default case, not general if/else blocks.Fixes #23155
Original prompt
This section details on the original issue you should resolve
<issue_title>Exhaustiveness checking against an enum only works when the enum has >1 member.</issue_title>
<issue_description>
TypeScript Version: [email protected]
Search Terms: discriminated, exhaustiveness, type guard, narrowing
Code
Expected behavior: No error would be thrown, as the switch statement is exhaustive. If the ActionTypes.DECREMENT parts are uncommented (resulting in two possible values for ActionTypes) there is no error. An error only occurs when ActionTypes takes on a single value. The error occurs even if the
neverassertion happens in the default statement, which is obviously unreachable from IIncrement.Actual behavior: An error is thrown despite the only possible value being explicitly handled. If ActionTypes.DECREMENT is uncommented the expected behavior is present.
Playground Link: (fixed the links)
[Error](https://www.typescriptlang.org/play/index.html#src=%2F%2F%20Legal%20action%20types%20for%20ValidAction%0D%0Aenum%20ActionTypes%20%7B%0D%0A%20%20INCREMENT%20%3D%20'INCREMENT'%2C%0D%0A%2F%2F%20%20%20DECREMENT%20%3D%20'DECREMENT'%2C%0D%0A%7D%0D%0A%0D%0Ainterface%20IIncrement%20%7B%0D%0A%20%20payload%3A%20%7B%7D%3B%0D%0A%20%20type%3A%20ActionTypes.INCREMENT%3B%0D%0A%7D%0D%0A%0D%0A%2F%2F%20interface%20IDecrement%20%7B%0D%0A%2F%2F%20%20%20payload%3A%20%7B%7D%3B%0D%0A%2F%2F%20%20%20type%3A%20ActionTypes.DECREMENT%3B%0D%0A%2F%2F%20%7D%0D%0A%0D%0A%2F%2F%20Any%20string%20not%20present%20in%20T%0D%0Atype%20AnyStringExcept%3CT%20extends%20string%3E%20%3D%20%7B%20%5BP%20in%20T%5D%3A%20never%3B%20%7D%3B%0D%0A%0D%0A%2F%2F%20ValidAction%20is%20an%20interface%20with%20a%20type%20in%20ActionTypes%0D%0Atype%20ValidAction%20%3D%20IIncrement%3B%0D%0A%2F%2F%20type%20ValidAction%20%3D%20IIncrement%20%7C%20IDecrement%3B%0D%0A%0D%0A%2F%2F%20UnhandledAction%20in%20an%20interface%20with%20a%20type%20that%20is%20not%20within%20ActionTypes%0D%0Atype%20UnhandledAction%20%3D%20%7B%20type%3A%20AnyStringExcept%3CActionTypes%3E%3B%20%7D%3B%0D%0A%0D%0A%2F%2F%20The%20set%20of%20all%20actions%0D%0Atype%20PossibleAction%20%3D%20ValidAction%20%7C%20UnhandledAction%3B%0D%0A%0D%0A%2F%2F%20Discriminates%20to%20ValidAction%0D%0Afunction%20isUnhandled(x%3A%20PossibleAction)%3A%20x%20is%20UnhandledAction%20%7B%0D%0A%20%20%20%20return%20!(x.type%20in%20ActionTypes)%3B%0D%0A%7D%0D%0A%0D%0Atype%20CounterState%20%3D%20number%3B%0D%0Aconst%20initialState%3A%20CounterState%20%3D%200%3B%0D%0A%0D%0Afunction%20receiveAction(state%20%3D%20initialState%2C%20action%3A%20PossibleAction)%20%7B%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20PossibleAction%0D%0A%20%20%20%20if%20(isUnhandled(action))%20%7B%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20UnhandledAction%0D%0A%20%20%20%20%20%20%20%20return%20state%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20ValidActio...
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.