ts-pattern
ts-pattern copied to clipboard
`exhaustive` doesn't work when the conditions use an enum with numeric values
Describe the bug
If you try to match over a type who's discriminating field is an enum with numeric values, exhaustive() seems to get lost and produces a type error when it's not needed.
Code Sandbox with a minimal reproduction case https://www.typescriptlang.org/play?target=9&jsx=0&module=7#code/JYWwDg9gTgLgBAbziAhjAxgCzgXzgMyghDgCIYBnAWjDRgFMoA7UgbgCh36mBXEgFUzAmAc34BPMPUTs4cAGIBJAEoBlfgBpZcVQFEAwgHkAcgBF2OTsIZR8KdNPnAoFGIOEiZcmJPoAuOHdRCSkAOiU1fg5LdmtGOwcdenQIJgATIM8EbR8pAMyQ+lC9IzNozlzpTLgAXgVnV2qAHySU9MyOdjTkgBsUKGk21zh4fKFRTtQMTAAKGABKbVCAd2AYWaRKsY9C8JV1XA0CWoA+OCGIHqKeiBEZ0nwGmFJ5xbkVtY2R323g32KDCZTIdjjUzhcrqEbndSBRkqk0i83nBQvQAB6YFA8VzAABu9Bm8w4QA
Workaround
Defining the enum values as string fixes the problem. Defining them as number (explicitly), doesn't.
With string: https://www.typescriptlang.org/play?target=9&jsx=0&module=7#code/JYWwDg9gTgLgBAbziAhjAxgCzgXzgMyghDgCIYBnAWjDRgFMoA7UgbgCh36mBXEgFUzAmAc34BPMPUTs4cAGIBJAEoBlfnAC8ZJWv4B9fgAlFAOQDihgJoAFAKKkANLLiq7AYQDypgCJaybl6+hiYW1vak7DicwgxQ+Cjo0vLAUBQwgsIiMnIwkvQAXHCZohJSAHS66hzR7LGMCUmu9OgQTAAmJdkILnlSRV1l9OWB3j41nH3SXf4paRlConAAPs2tHV0c7O0tADYoUNLr6XDwA4siW6gYmAAUMACULuUA7sAwd0hT51lDlSrqXCOAhaAB8cGOEF2w12EBEt1I+FS6VIDyecle70+p3yP1K+RGHjGQJBmnBkOh5Vh8NIFBabXaqPRcHK9AAHpgUDx0sAAG70W4PDhAA
Versions
- TypeScript version: 5.1.6
- ts-pattern version: 5.0.4
- environment: node 20.5.0, ts's playground, and anything I tried.
Somewhat offtopic
This library is impressive! Thanks for building it.
Looks like a duplicate of #168.
See also #58 and #162.
Link to underlying ts bug/issue/defect/whatever https://github.com/microsoft/TypeScript/issues/46562
Thanks! That's correct.
What about adding a "Limitations" section to the readme? This is really surprising.
I understand it's a TS limitation, but it must be hitting many (potential) users.
if you can't simply migrate to a string enum, there's a "dirty" (typesafe) workaround using numbers, just explicitly cast the enum value
match(t)
.with({ type: ThingType.FIRST as 0 }, f => console.log("first"))
.with({ type: ThingType.SECOND as 1 }, f => console.log("second"))
.exhaustive();