The exhaustive Pattern Matching library for TypeScript with smart type inference
—
Comprehensive pattern construction utilities including wildcards, type-specific patterns, and combinators for building complex matching logic.
Basic patterns that match entire categories of values.
/**
* Wildcard pattern matching any value
*/
const any: AnyPattern;
/**
* Alias for any wildcard pattern
*/
const _: AnyPattern;
/**
* String wildcard with chainable methods
*/
const string: StringPattern;
/**
* Number wildcard with chainable methods
*/
const number: NumberPattern;
/**
* BigInt wildcard with chainable methods
*/
const bigint: BigIntPattern;
/**
* Boolean wildcard pattern
*/
const boolean: BooleanPattern;
/**
* Symbol wildcard pattern
*/
const symbol: SymbolPattern;
/**
* Matches null or undefined values
*/
const nullish: NullishPattern;
/**
* Matches any non-null, non-undefined value
*/
const nonNullable: NonNullablePattern;
/**
* Symbol used for the matcher protocol to create custom patterns
*/
const matcher: unique symbol;Usage Examples:
import { match, P } from "ts-pattern";
// Basic wildcards
const handleValue = match(input)
.with(P.string, (str) => `string: ${str}`)
.with(P.number, (num) => `number: ${num}`)
.with(P.boolean, (bool) => `boolean: ${bool}`)
.with(P.nullish, () => 'null or undefined')
.with(P._, () => 'something else');Patterns for matching strings with specific characteristics.
interface StringChainable<pattern> extends Chainable<pattern> {
/**
* Matches strings starting with prefix
* @param prefix - Required string prefix
*/
startsWith(prefix: string): StringChainable<GuardP<any, `${string}${string}`>>;
/**
* Matches strings ending with suffix
* @param suffix - Required string suffix
*/
endsWith(suffix: string): StringChainable<GuardP<any, `${string}${string}`>>;
/**
* Matches strings with minimum length
* @param min - Minimum string length
*/
minLength(min: number): StringChainable<GuardP<any, string>>;
/**
* Matches strings with exact length
* @param len - Exact string length
*/
length(len: number): StringChainable<GuardP<any, string>>;
/**
* Matches strings with maximum length
* @param max - Maximum string length
*/
maxLength(max: number): StringChainable<GuardP<any, string>>;
/**
* Matches strings containing substring
* @param substring - Required substring
*/
includes(substring: string): StringChainable<GuardP<any, string>>;
/**
* Matches strings against regular expression
* @param expression - Regular expression pattern
*/
regex(expression: string | RegExp): StringChainable<GuardP<any, string>>;
}Usage Examples:
// String pattern chaining
const validateInput = match(userInput)
.with(P.string.startsWith('http').includes('://'), (url) => validateUrl(url))
.with(P.string.regex(/^\d{3}-\d{2}-\d{4}$/), (ssn) => validateSSN(ssn))
.with(P.string.minLength(8).maxLength(32), (password) => validatePassword(password))
.otherwise(() => 'invalid input');Patterns for matching numbers with specific constraints.
interface NumberChainable<pattern> extends Chainable<pattern> {
/**
* Matches numbers between min and max (inclusive)
* @param min - Minimum value (inclusive)
* @param max - Maximum value (inclusive)
*/
between(min: number, max: number): NumberChainable<GuardP<any, number>>;
/**
* Matches numbers less than max
* @param max - Maximum value (exclusive)
*/
lt(max: number): NumberChainable<GuardP<any, number>>;
/**
* Matches numbers greater than min
* @param min - Minimum value (exclusive)
*/
gt(min: number): NumberChainable<GuardP<any, number>>;
/**
* Matches numbers less than or equal to max
* @param max - Maximum value (inclusive)
*/
lte(max: number): NumberChainable<GuardP<any, number>>;
/**
* Matches numbers greater than or equal to min
* @param min - Minimum value (inclusive)
*/
gte(min: number): NumberChainable<GuardP<any, number>>;
/**
* Matches integer numbers only
*/
int(): NumberChainable<GuardP<any, number>>;
/**
* Matches finite numbers (excludes Infinity and -Infinity)
*/
finite(): NumberChainable<GuardP<any, number>>;
/**
* Matches positive numbers (> 0)
*/
positive(): NumberChainable<GuardP<any, number>>;
/**
* Matches negative numbers (< 0)
*/
negative(): NumberChainable<GuardP<any, number>>;
}Usage Examples:
// Number pattern chaining
const categorizeNumber = match(value)
.with(P.number.int().positive(), (n) => `positive integer: ${n}`)
.with(P.number.between(0, 100), (n) => `percentage: ${n}%`)
.with(P.number.finite().negative(), (n) => `negative: ${n}`)
.otherwise(() => 'other number');Patterns for matching bigint values with constraints.
interface BigIntChainable<pattern> extends Chainable<pattern> {
/**
* Matches bigints between min and max (inclusive)
*/
between(min: bigint, max: bigint): BigIntChainable<GuardP<any, bigint>>;
/**
* Matches bigints less than max
*/
lt(max: bigint): BigIntChainable<GuardP<any, bigint>>;
/**
* Matches bigints greater than min
*/
gt(min: bigint): BigIntChainable<GuardP<any, bigint>>;
/**
* Matches bigints less than or equal to max
*/
lte(max: bigint): BigIntChainable<GuardP<any, bigint>>;
/**
* Matches bigints greater than or equal to min
*/
gte(min: bigint): BigIntChainable<GuardP<any, bigint>>;
/**
* Matches positive bigints (> 0n)
*/
positive(): BigIntChainable<GuardP<any, bigint>>;
/**
* Matches negative bigints (< 0n)
*/
negative(): BigIntChainable<GuardP<any, bigint>>;
}Patterns for matching arrays, sets, and maps.
/**
* Matches arrays with optional element pattern
* @param pattern - Pattern to match array elements against
* @returns Array pattern that can be chained
*/
function array<input, const pattern extends Pattern<UnwrapArray<input>>>(
pattern?: pattern
): ArrayChainable<ArrayP<input, pattern>>;
/**
* Matches sets with optional element pattern
* @param pattern - Pattern to match set elements against
* @returns Set pattern that can be chained
*/
function set<input, const pattern extends Pattern<UnwrapSet<input>>>(
pattern?: pattern
): Chainable<SetP<input, pattern>>;
/**
* Matches maps with optional key and value patterns
* @param keyPattern - Pattern to match map keys against
* @param valuePattern - Pattern to match map values against
* @returns Map pattern that can be chained
*/
function map<
input,
const keyPattern extends Pattern<UnwrapMapKey<input>>,
const valuePattern extends Pattern<UnwrapMapValue<input>>
>(
keyPattern?: keyPattern,
valuePattern?: valuePattern
): Chainable<MapP<input, keyPattern, valuePattern>>;
interface ArrayChainable<pattern> extends Chainable<pattern> {
/**
* Makes the array pattern optional
*/
optional(): ArrayChainable<OptionalP<any, pattern>>;
/**
* Selects the matched array elements
*/
select(key?: string): ArrayChainable<SelectP<string, any, pattern>>;
}Usage Examples:
// Collection patterns
const processData = match(input)
.with(P.array(P.string), (strings) => strings.join(','))
.with(P.array(P.number.positive()), (nums) => nums.reduce((a, b) => a + b))
.with(P.set(P.string.startsWith('user:')), (userIds) => loadUsers(userIds))
.with(P.map(P.string, P.number), (stringToNum) => Object.fromEntries(stringToNum))
.otherwise(() => 'unsupported data structure');Patterns that combine multiple patterns using logical operations.
/**
* Creates a pattern that matches if ALL patterns match (AND logic)
* @param patterns - Patterns that must all match
* @returns Intersection pattern
*/
function intersection<
input,
const patterns extends readonly [Pattern<input>, ...Pattern<input>[]]
>(...patterns: patterns): Chainable<AndP<input, patterns>>;
/**
* Creates a pattern that matches if ANY pattern matches (OR logic)
* @param patterns - Patterns where at least one must match
* @returns Union pattern
*/
function union<
input,
const patterns extends readonly [Pattern<input>, ...Pattern<input>[]]
>(...patterns: patterns): Chainable<OrP<input, patterns>>;
/**
* Creates a pattern that matches if the sub-pattern does NOT match
* @param pattern - Pattern to negate
* @returns Negation pattern
*/
function not<input, const pattern extends Pattern<input>>(
pattern: pattern
): Chainable<NotP<input, pattern>>;Usage Examples:
// Combinatorial patterns
const validateUser = match(user)
.with(
P.intersection(
{ age: P.number.gte(21) },
{ permissions: P.array(P.string.includes('admin')) },
{ active: true }
),
(u) => 'admin user'
)
.with(
P.union(
{ type: 'guest' },
{ type: 'trial', expiresAt: P.when(d => d > new Date()) }
),
(u) => 'temporary access'
)
.with(
P.not({ banned: true }),
(u) => 'regular user'
)
.otherwise(() => 'access denied');Patterns that use predicates and guards for custom matching logic.
/**
* Creates a pattern that matches based on predicate function
* @param predicate - Function that returns truthy value for match
* @returns Guard pattern with type narrowing
*/
function when<input, predicate extends (value: input) => unknown>(
predicate: predicate
): GuardP<
input,
predicate extends (value: any) => value is infer narrowed ? narrowed : never
>;
/**
* Creates a pattern that matches instances of a class
* @param classConstructor - Constructor function to match against
* @returns Instance pattern
*/
function instanceOf<T extends AnyConstructor>(
classConstructor: T
): Chainable<GuardP<unknown, InstanceType<T>>>;
/**
* Enables chainable methods on structural patterns
* @param pattern - Structural pattern to make chainable
* @returns Chainable pattern with structural matching
*/
function shape<input, const pattern extends Pattern<input>>(
pattern: pattern
): Chainable<GuardP<input, InvertPattern<pattern, input>>>;Usage Examples:
// Conditional patterns
const handleError = match(error)
.with(P.instanceOf(TypeError), (err) => handleTypeError(err))
.with(P.instanceOf(RangeError), (err) => handleRangeError(err))
.with(P.when(err => err.code === 'ENOENT'), (err) => handleFileNotFound(err))
.with(P.shape({ status: P.number.between(400, 499) }).select(), (err) => handleClientError(err))
.otherwise(err => handleGenericError(err));Patterns for handling optional values and capturing selections.
/**
* Makes a pattern optional (matches undefined or the pattern)
* @param pattern - Pattern to make optional
* @returns Optional pattern
*/
function optional<input, const pattern extends Pattern<input>>(
pattern: pattern
): Chainable<OptionalP<input, pattern>, 'optional'>;
/**
* Captures the matched value for use in handler
* @param key - Optional key name for named selection
* @param pattern - Optional pattern to match before selection
* @returns Selection pattern
*/
function select(): Chainable<AnonymousSelectP, 'select' | 'or' | 'and'>;
function select<input, const pattern extends Pattern<input>>(
pattern: pattern
): Chainable<SelectP<symbols.anonymousSelectKey, input, pattern>, 'select' | 'or' | 'and'>;
function select<input, const pattern extends Pattern<input>>(
key: string,
pattern: pattern
): Chainable<SelectP<string, input, pattern>, 'select' | 'or' | 'and'>;Usage Examples:
// Optional and selection patterns
const processConfig = match(config)
.with(
{
host: P.string,
port: P.number.optional(),
credentials: P.select('creds', { username: P.string, password: P.string })
},
({ creds }) => connectWithCredentials(creds.username, creds.password)
)
.with(
{ host: P.string, apiKey: P.select() },
(apiKey) => connectWithApiKey(apiKey)
)
.otherwise(() => 'invalid configuration');Methods available on most patterns for additional composition.
interface Chainable<pattern, excluded = never> {
/**
* Makes the pattern optional (if not excluded)
*/
optional(): excluded extends 'optional' ? never : Chainable<OptionalP<any, pattern>>;
/**
* Creates intersection with another pattern (if not excluded)
*/
and<P2>(pattern: P2): excluded extends 'and' ? never : Chainable<AndP<any, [pattern, P2]>>;
/**
* Creates union with another pattern (if not excluded)
*/
or<P2>(pattern: P2): excluded extends 'or' ? never : Chainable<OrP<any, [pattern, P2]>>;
/**
* Captures matched value (if not excluded)
*/
select(key?: string): excluded extends 'select' ? never : Chainable<SelectP<string, any, pattern>>;
}Install with Tessl CLI
npx tessl i tessl/npm-ts-pattern