Skip to Content
Wildcards

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

  1. Use specific wildcards: Prefer P.string over P._ when you know the type
  2. Combine with literals: Mix wildcards and literal patterns for flexibility
  3. 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();