The exhaustive Pattern Matching library for TypeScript with smart type inference
—
Core pattern matching functionality for creating exhaustive conditional logic with type safety and smart type inference.
Creates a chainable pattern matching expression for handling complex conditional logic.
/**
* Creates a pattern matching expression
* @param value - Input value to match against patterns
* @returns Match expression for chaining patterns
*/
function match<const input, output = unknown>(
value: input
): Match<input, output>;Usage Examples:
import { match, P } from "ts-pattern";
// Basic pattern matching
const result = match(value)
.with('hello', () => 'greeting')
.with('goodbye', () => 'farewell')
.otherwise(() => 'unknown');
// Object pattern matching
const response = match(apiResponse)
.with({ status: 'success', data: P.select() }, (data) => data)
.with({ status: 'error', message: P.select() }, (msg) => { throw new Error(msg) })
.exhaustive();Methods available on the match expression for building pattern matching logic.
interface Match<input, output> {
/**
* Match against a pattern with handler function
* @param pattern - Pattern to match against input
* @param handler - Function to execute on match
* @returns Updated match expression
*/
with<const pattern extends Pattern<input>>(
pattern: pattern,
handler: (selections: P.infer<pattern>, value: input) => output
): Match<input, output>;
/**
* Match against a pattern with guard condition and handler
* @param pattern - Pattern to match against input
* @param guard - Additional condition function
* @param handler - Function to execute on match
* @returns Updated match expression
*/
with<const pattern extends Pattern<input>>(
pattern: pattern,
guard: (value: input) => unknown,
handler: (selections: P.infer<pattern>, value: input) => output
): Match<input, output>;
/**
* Match against multiple patterns with single handler
* @param patterns - Multiple patterns followed by handler function
* @returns Updated match expression
*/
with<const patterns extends readonly Pattern<input>[]>(
...args: [...patterns, (selections: any, value: input) => output]
): Match<input, output>;
/**
* Match based on predicate function
* @param predicate - Function returning truthy value for match
* @param handler - Function to execute on match
* @returns Updated match expression
*/
when(
predicate: (value: input) => unknown,
handler: (value: input) => output
): Match<input, output>;
/**
* Provide default case and return result
* @param handler - Default handler function
* @returns Final result of pattern matching
*/
otherwise(handler: (value: input) => output): output;
/**
* Ensure exhaustive matching and return result
* @param unexpectedValueHandler - Optional error handler for unmatched cases
* @returns Final result of pattern matching
* @throws NonExhaustiveError if no patterns match
*/
exhaustive(unexpectedValueHandler?: (value: input) => never): output;
/**
* Alias for exhaustive()
* @returns Final result of pattern matching
*/
run(): output;
/**
* Type-level method for return type inference
* @returns Match expression for type inference
*/
returnType(): Match<input, output>;
/**
* Type-level method for input type narrowing
* @returns Match expression for type narrowing
*/
narrow(): Match<input, output>;
}Usage Examples:
// Multiple patterns with single handler
const category = match(statusCode)
.with(200, 201, 202, () => 'success')
.with(400, 401, 403, 404, () => 'client-error')
.with(500, 502, 503, () => 'server-error')
.otherwise(() => 'unknown');
// Guard conditions
const processUser = match(user)
.with(
{ type: 'admin' },
(u) => u.permissions.includes('delete'),
(u) => deleteUser(u)
)
.with({ type: 'user', active: true }, (u) => processActiveUser(u))
.otherwise(() => 'inactive or unauthorized');
// Predicate matching
const handleAge = match(person)
.when(p => p.age >= 18, (p) => 'adult: ' + p.name)
.when(p => p.age >= 13, (p) => 'teen: ' + p.name)
.otherwise(p => 'child: ' + p.name);Core pattern types used in matching expressions.
/**
* Base pattern type that can match values of type T
*/
type Pattern<T> =
| T
| Matcher<any, any, any, any, any>
| (T extends readonly (infer U)[] ? Pattern<U>[] : never)
| (T extends object ? { [K in keyof T]?: Pattern<T[K]> } : never);
/**
* Matcher protocol interface for custom patterns
*/
interface Matcher<
input,
pattern,
narrowedOrFn,
exclude = never,
matcherType = string
> {
[matcher](): {
match: (value: input) => { matched: boolean; selections?: Record<string, unknown> };
getSelectionKeys?: () => string[];
matcherType?: matcherType;
};
}
/**
* Symbol used for matcher protocol
*/
declare const matcher: unique symbol;Pattern matching supports value selection and provides smart type inference.
/**
* Infer the type of value matched by a pattern
*/
type infer<pattern> = InvertPattern<NoInfer<pattern>, unknown>;
/**
* Narrow input type to pattern-compatible subset
*/
type narrow<input, pattern> = ExtractPreciseValue<
input,
InvertPattern<pattern, input>
>;Usage Examples:
// Type inference with selections
const extractData = match(response)
.with({ success: true, data: P.select() }, (data) => {
// data is properly typed based on the pattern
return data;
})
.with({ success: false, error: P.select() }, (error) => {
// error is properly typed
throw error;
})
.exhaustive();
// Named selections
const processEvent = match(event)
.with(
{ type: 'user', action: 'login', userId: P.select('id'), timestamp: P.select('time') },
({ id, time }) => logUserLogin(id, time)
)
.with(
{ type: 'system', level: 'error', message: P.select() },
(message) => handleSystemError(message)
)
.exhaustive();Install with Tessl CLI
npx tessl i tessl/npm-ts-pattern