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

Preventing use of .exhaustive() for enum checks #205

Open
tbarusseau opened this issue Nov 22, 2023 · 1 comment
Open

Preventing use of .exhaustive() for enum checks #205

tbarusseau opened this issue Nov 22, 2023 · 1 comment
Labels
enhancement New feature or request

Comments

@tbarusseau
Copy link

Is your feature request related to a problem? Please describe.
Using enums inside .match allows you to check for exhaustiveness using .exhaustive(). However, this leads to an annoying scenario where a client of your API might use .exhaustive() on a non-exhaustive enum you declared, breaking their project at compile-time if you update that specific enum to add a new field (which in many cases could be considered non-breaking).

Describe the solution you'd like
Having a way to prevent to use .exhaustive() on an enum match would force clients to implement a fallback handling using .otherwise, which can be great when you deal with enums that aren't yet complete (and shouldn't be considered as such).

Note that I have absolutely no idea whether it can be implemented on the ts-pattern side or not. I'm not familiar at all with the codebase, but I noticed that this wasn't discussed before.

Describe alternatives you've considered
Source of inspiration: #[non_exhaustive] in Rust, which obviously cannot be applied in this case considering ts-pattern is at the other side of the problem!

@tbarusseau tbarusseau added the enhancement New feature or request label Nov 22, 2023
@philer
Copy link
Contributor

philer commented Nov 28, 2023

I'm not a dev on this project but an avid user and I'd just like to throw in my concerns with this suggestion.

I use .exhaustive() extensively on enums and, more commonly, string literal unions. This is very useful for a simple reason: When I extend the enum/union the compiler will tell me all the places that I need to update. If I use .otherwise() instead, the compiler will tell me nothing. The fallback will prevent my code from crashing but it will not make it correct. Instead I may only later find out all the places that I've forgotten to treat the additional option. In short, this prevents the type checker from warning me about potential bugs, which is the whole point of using typescript.
This applies both to enums/unions that are defined in my own code and those defined in libraries/APIs.

Also bear in mind that there are plenty of use cases where there is no real meaningful default/fallback. Consider classic enum examples like day of the week, months etc. Forcing library/API users to use .otherwise() may very well provoke practices of implementing a fallback runtime error, like .otherwise(() => { throw new Error("Unexpected enum value") }, effectively forfeiting the benefit of static exhaustiveness checking in favor of a crash.

Extending an exported enum on a library/API is, strictly speaking, a breaking change on the type level. Marking it as such can be a burden but it can not be circumvented by asking independent tools like ts-pattern to enforce runtime checking. Consider that there are plenty of alternatives to ts-pattern where static exhaustiveness comes into play, including the native switch statement or even simple if/else chaining.

My recommendation would be this: As a library/API author, document your enums/unions to indicate if developers should expect it to be extended in the future. Then leave the choice of whether to use .exhaustive() or .otherwise() to them and trust them to make a competent decision for their individual use case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants