ts-pattern v3
This versions introduces a few breaking change for the sake of a better general developer experience.
.exhaustive()
now ends the pattern matching expression
with v2
const f = (x: 1 | 2 | 3) =>
match(x)
.exhaustive()
.with(1, () => 'one')
.with(2, () => 'two')
.with(3, () => 'three')
.run();
with v3
const f = (x: 1 | 2 | 3) =>
match(x)
.with(1, () => 'one')
.with(2, () => 'two')
.with(3, () => 'three')
.exhaustive();
selections are now passed as first argument to the handler
with v2
type Value = { type: 'vec2', x: number, y: number } | { type: 'number', value: number }
const f = (x: [Value, Value]) =>
match(x)
.with([{ type: 'vec2', x: select('x'), y: select('y') }, __], (_, { x, y }) => ...)
...
.run();
with v3
type Value = { type: 'vec2', x: number, y: number } | { type: 'number', value: number }
const f = (x: [Value, Value]) =>
match(x)
.with([{ type: 'vec2', x: select('x'), y: select('y') }, __], ({ x, y }) => ...)
...
.exhaustive();
Anonymous selection support
ts-pattern now supports anonymous selection for when you want to extract a single value from your pattern:
with v2
// Not possible
with v3
type Value = { type: 'vec2', x: number, y: number } | { type: 'number', value: number }
const f = (x: Value) =>
match(x)
.with({ type: 'number', value: select() }, (value) => /* value: number */)
// you can't have several anonymous `select()` in the same pattern. This is a type error:
.with({ type: 'vec2', x: select(), y: select() }, (value) => /* value: SeleveralAnonymousSelectError */)
.exhaustive();
Support for when
clauses within .exhaustive()
match expressions
when(predicate)
patterns and match(...).when(predicate)
are now permitted within .exhaustive()
match expressions.
with v2
// Not possible
with v3
If your predicate is a type guard function, the case will be considered handled:
type Input = 'a' | 'b'
match<Input>('a')
.when((x): x is 'a' => x === 'a' , () => {...})
.when((x): x is 'b' => x === 'b' , () => {...})
.exhaustive(); // This compiles
match<Input>('a')
.when((x): x is 'a' => x === 'a' , () => {...})
// This doesn't compiles
.exhaustive();
But if your predicate isn't a type guard, exhaustive checking will consider that this clause never matches anything:
match<Input>('a')
.when((x): x is 'a' => x === 'a' , () => {...})
.when(x => x === 'b' , () => {...})
// This doesn't compiles, because ts-pattern has no way to know that the 'b' case is handled
.exhaustive();
It works similarily with the when()
helper function:
type input = { type: 'success', data: string[] } | { type: 'error' }
match(input)
.with({ type: 'success', data: when(xs => xs.length > 0) }, () => {...})
.with({ type: 'error' }, () => {...})
// this doesn't compile, { type: 'success' } with an empty data array is not handled
.exhaustive();
match(input)
.with({ type: 'success', data: when(xs => xs.length > 0) }, () => {...})
.with({ type: 'success' }, () => {...})
.with({ type: 'error' }, () => {...})
.exhaustive(); // this compiles