NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #3978 most downloaded on npm
The exhaustive Pattern Matching library for TypeScript.
Last release 11 months ago
26 Oct 2025
Ships fairly regularly
a new release about every 2 months
Most releases are documented
notes for 47 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
6 years old
159 releases · first in 2020
One column per quarter.
You can now provide up to 5 patterns to .with(), and the code branch will be chosen if your input matches one of these patterns.
You can now provide up to 5 patterns to .with(), and the code branch will be chosen if your input matches one of these patterns.
Example:
match<Country>('France')
.exhaustive()
// if it matches one of these patterns
.with('France', 'Germany', 'Spain', () => 'Europe')
.with('USA', () => 'America')
.run();
match<Country>('France')
.exhaustive()
.with('Germany', 'Spain', () => 'Europe')
.with('USA', () => 'America')
// doesn't compile! 'France' is missing
.run();
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
The selection object you get in second parameter has a better type inference
Bug Fixes:
selection object you get in second parameter has a better type inferenceGeneral improvements:
Expect and Equal type utility functionsThis release features a major refactoring of the way exhaustive pattern matching is enforced, that should drastically improve its compile time perform
This release features a major refactoring of the way exhaustive pattern matching is enforced, that should drastically improve its compile time performances on medium to large input types.
.exhaustive() used to transform the input type in a flat union type, containing all the possible combination of all unions contained in the input type. For instance a type like:
type Input = {type: "a", mode: "b" | "c" | "d"} | {type: "b", mode: "f" | "g"}
Would be turned into a type like this:
type DistributedInput =
| {type: "a", mode: "b"}
| {type: "a", mode: "c"}
| {type: "a", mode: "d"}
| {type: "b", mode: "f"}
| {type: "b", mode: "g"}
This solution was working fine, but the downside is that sometimes your Input type contains some huge unions that you never (or rarely) want to match against. Here is an example:
type CSSColor = "grey" | "red" | "yellow" | "blue" | "green" | ...; // hundreds of color names
type Input =
| { type: "text"; color: CSSColor }
| { type: "button"; color: CSSColor; backgroundColor: CSSColor };
match(input)
.exhaustive() // "union type that is too complex to represent"
.with({ type: "button" }, () => ...)
.with({ type: "text" }, () => ...)
.run();
We hit the union limit of 500 items, even though we actually didn't need to transform the input type at all since we were only matching against its type property.
Now exhaustive matching is a lot smarter because it only computes the combination of unions against which you are really matching, which means that in the case described above the input type is unchanged:
match(input)
.exhaustive() // nothing happens at this point, `Input` stay the same.
.with({ type: "button" }, () => ...) // we match on the `type` property, we don't need to change `Input`.
.with({ type: "text" }, () => ...) // same as above.
.run(); // it works
If we were matching on a specific color, though, we would have to distribute the CSSColor union over the Input type:
match(input)
.exhaustive() // input: Input
.with({ type: "button" }, () => ...) // input: Input
// below we are matching on both `type` and `color`, we need to
// distribute matched unions over the Input type:
.with({ type: "text", color: "blue" }, () => ...)
/* input: | { type: "button"; color: CSSColor; backgroundColor: CSSColor }
* | { type: "text", color: "grey" }
* | { type: "text", color: "red" }
* | { type: "text", color: "yellow" }
* | ... all possible colors except "blue". We need to distribute at this point,
* but note that the `type: "button"` case hasn't been distributed,
* so we don't reach the `union too complex to represent` limit.
*/
.run();
You can find more details in this issue https://github.com/gvergnaud/ts-pattern/issues/16 from @m-rutter
This pre-release features a major refactoring of the way exhaustive pattern matching is enforced, that should drastically improve its compile time per
This pre-release features a major refactoring of the way exhaustive pattern matching is enforced, that should drastically improve its compile time performances on medium to large input types.
.exhaustive() used to transform the input type in a flat union type, containing all the possible combination of all unions contained in the input type. For instance a type like:
type Input = {type: "a", mode: "b" | "c" | "d"} | {type: "b", mode: "f" | "g"}
Would be turned into a type like this:
type DistributedInput =
| {type: "a", mode: "b"}
| {type: "a", mode: "c"}
| {type: "a", mode: "d"}
| {type: "b", mode: "f"}
| {type: "b", mode: "g"}
This solution was working fine, but the downside is that sometimes your Input type contains some huge unions that you never (or rarely) want to match against. Here is an example:
type CSSColor = "grey" | "red" | "yellow" | "blue" | "green" | ...; // hundreds of color names
type Input =
| { type: "text"; color: CSSColor }
| { type: "button"; color: CSSColor; backgroundColor: CSSColor };
match(input)
.exhaustive() // "union type that is too complex to represent"
.with({ type: "button" }, () => ...)
.with({ type: "text" }, () => ...)
.run();
We hit the union limit of 500 items, even though we actually didn't need to transform the input type at all since we were only matching against its type property.
Now exhaustive matching is a lot smarter because it only computes the combination of unions against which you are really matching, which means that in the case described above the input type is unchanged:
match(input)
.exhaustive() // nothing happens at this point, `Input` stay the same.
.with({ type: "button" }, () => ...) // we match on the `type` property, we don't need to change `Input`.
.with({ type: "text" }, () => ...) // same as above.
.run(); // it works
If we were matching on a specific color, though, we would have to distribute the CSSColor union over the Input type:
match(input)
.exhaustive() // input: Input
.with({ type: "button" }, () => ...) // input: Input
// below we are matching on both `type` and `color`, we need to
// distribute matched unions over the Input type:
.with({ type: "text", color: "blue" }, () => ...)
/* input: | { type: "button"; color: CSSColor; backgroundColor: CSSColor }
* | { type: "text", color: "grey" }
* | { type: "text", color: "red" }
* | { type: "text", color: "yellow" }
* | ... all possible colors except "blue". We need to distribute at this point,
* but note that the `type: "button"` case hasn't been distributed,
* so we don't reach the `union too complex to represent` limit.
*/
.run();
This change is pretty significant so please try it and tell me if it's working as expected!
You can find more details in this issue https://github.com/gvergnaud/ts-pattern/issues/16 from @m-rutter
Improve support for generic types. Some type expressions containing generics did not reduce properly, even with simple catch all patterns. Now they do
This patch contains some small type checking perf improvements:
This patch contains some small type checking perf improvements:
Error, Date, RegExp, Function, etc.Nothing published for this version
This version introduces a new API to opt into exhaustive pattern matching, to let typescript make sure that all possible cases are handled and that yo
This version introduces a new API to opt into exhaustive pattern matching, to let typescript make sure that all possible cases are handled and that your code won't throw at runtime.
Here is an example of this new API:
type Input = { kind: 'none' } | { kind: 'some'; value: number };
// This compiles: match<Input>({ kind: 'some', value: 3 }) .exhaustive() .with({ kind: 'some' }, ({ value }) => value) .with({ kind: 'none' }, () => 0) .run();
// This doesn't match<Input>({ kind: 'some', value: 3 }) .exhaustive() .with({ kind: 'some' }, ({ value }) => value) .run();
This is a major version because it uses some features of the type system that were introduced in TS v4.x, like variadic tuples, and recursive conditional types.
This pre-release introduces a new API to opt into exhaustive checking, to let typescript make sure that all possible cases are handled and that your c
This pre-release introduces a new API to opt into exhaustive checking, to let typescript make sure that all possible cases are handled and that your code won't throw at runtime.
Here is an example of this new API:
type Input = { kind: 'none' } | { kind: 'some'; value: number };
// This compiles:
match<Input>({ kind: 'some', value: 3 })
.exhaustive()
.with({ kind: 'some' }, ({ value }) => value)
.with({ kind: 'none' }, () => 0)
.run();
// This doesn't
match<Input>({ kind: 'some', value: 3 })
.exhaustive()
.with({ kind: 'some' }, ({ value }) => value)
.run();
This is a major version because it uses some features of the type system that were introduced in TS v4.x, like variadic tuples, and recursive conditional types. People using earlier versions of TS will need to upgrade in order to use ts-pattern v2+.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →