Last Updated: 3/6/2026
Wildcard Patterns
Wildcard patterns match values based on their type rather than their specific value.
P._ (Match Anything)
The P._ pattern will match any value. P.any is an alias for P._.
import { match, P } from 'ts-pattern';
const input = 'hello';
const output = match(input)
.with(P._, () => 'It will always match')
// OR
.with(P.any, () => 'It will always match')
.otherwise(() => 'This will never be used');
console.log(output); // => 'It will always match'P.string
Matches any string value.
const output = match(input)
.with('bonjour', () => 'French greeting')
.with(P.string, (str) => `Some string: ${str}`)
.exhaustive();P.number
Matches any number value.
const output = match<number | string>(input)
.with(P.string, () => 'it is a string!')
.with(P.number, (num) => `it is a number: ${num}`)
.exhaustive();P.boolean
Matches any boolean value.
const output = match<number | string | boolean>(input)
.with(P.string, () => 'it is a string!')
.with(P.number, () => 'it is a number!')
.with(P.boolean, (bool) => `it is a boolean: ${bool}`)
.exhaustive();P.bigint
Matches any bigint value.
const output = match<bigint | null>(input)
.with(P.bigint, (n) => `it is a bigint: ${n}`)
.otherwise(() => 'not a bigint');
console.log(output); // For 20000000n => 'it is a bigint: 20000000'P.symbol
Matches any symbol value.
const output = match<symbol | null>(input)
.with(P.symbol, () => 'it is a symbol!')
.otherwise(() => 'not a symbol');
const sym = Symbol('test');
console.log(match(sym).with(P.symbol, () => 'matched').exhaustive());
// => 'matched'P.nullish
Matches null or undefined.
const output = match<number | null | undefined>(input)
.with(P.number, (n) => `number: ${n}`)
.with(P.nullish, () => 'null or undefined')
.exhaustive();
console.log(match(null).with(P.nullish, () => 'matched').exhaustive());
// => 'matched'
console.log(match(undefined).with(P.nullish, () => 'matched').exhaustive());
// => 'matched'P.nonNullable
Matches any value except null or undefined.
const output = match<number | null | undefined>(input)
.with(P.nonNullable, (n) => `has a value: ${n}`)
.otherwise(() => 'null or undefined');
console.log(match(42).with(P.nonNullable, (n) => n).exhaustive());
// => 42
console.log(match(null).with(P.nonNullable, (n) => n).otherwise(() => 'nullish'));
// => 'nullish'Combining Wildcards
Wildcards can be used in nested patterns:
type Response =
| { status: 'success'; data: string }
| { status: 'error'; error: Error }
| { status: 'loading' };
const handleResponse = (response: Response) =>
match(response)
.with({ status: 'success', data: P.string }, (r) =>
`Success: ${r.data}`
)
.with({ status: 'error', error: P.instanceOf(Error) }, (r) =>
`Error: ${r.error.message}`
)
.with({ status: P._ }, () => 'Loading...')
.exhaustive();Type Inference
Wildcards provide strong type inference:
const value: unknown = 'hello';
match(value)
.with(P.string, (str) => {
// str is inferred as string
console.log(str.toUpperCase());
})
.with(P.number, (num) => {
// num is inferred as number
console.log(num.toFixed(2));
})
.otherwise(() => {});Best Practices
- Use specific wildcards: Prefer
P.stringoverP._when you know the type - Combine with literals: Mix wildcards and literal patterns for flexibility
- Leverage type narrowing: Let TypeScript infer precise types
// Good: Specific wildcards
match(input)
.with('special', () => 'special case')
.with(P.string, (s) => `other string: ${s}`)
.otherwise(() => 'not a string');
// Less ideal: Too general
match(input)
.with('special', () => 'special case')
.with(P._, (x) => `something: ${x}`) // Lost type information
.exhaustive();