Guide · Open source
How to use ts-pattern: match, with and exhaustive in TypeScript
Updated · written with AI from the project's own documentation and checked against our catalogue data
TS-Pattern is a TypeScript library for pattern matching. It lets you branch on the shape of your data with one compact match expression instead of long if/else or switch chains. This guide shows you how to install it, write a match expression, use .exhaustive() so the compiler flags unhandled cases, and apply it to a state reducer.
Quick start
- 1Install the package. Run npm install ts-pattern in your project, or use pnpm add ts-pattern if you use pnpm.
- 2Import match and P. Add import { match, P } from 'ts-pattern'; to the file where you branch on data.
- 3Model your data as a union. Describe each possible shape with a tagged union, such as objects with a type or status field.
- 4Start a match expression. Call match with the value you want to branch on.
- 5Add a .with clause per case. Pass a pattern and a handler for each case you need.
- 6End with .exhaustive(). Call .exhaustive() so the type checker flags any case you have not handled.
Install ts-pattern with npm, pnpm, yarn or bun
TS-Pattern is installed from npm. The simplest route is the npm command below. The README also lists pnpm, yarn and bun alternatives.
- Prerequisites: Node.js, or a compatible package manager such as npm, pnpm, yarn or bun.
- The README describes the library as only about 2kB, so the bundle cost is small.
npm install ts-patternImport match and P
Import the two things most examples use: match, which starts a match expression, and P, which provides patterns such as wildcards and selectors.
import { match, P } from 'ts-pattern';Write a match expression with .with and .exhaustive()
Call match with a value, then add one .with() clause per case. Each clause takes a pattern and a handler function. End the chain with .exhaustive() so the type checker reports any case you have not handled.
The README example below matches a Result type that is either ok or error, and handles the nested data type inside the ok case.
- Put the pattern in the first argument of .with().
- The handler receives the matched value, or the value picked by P.select().
const html = match(result)
.with({ type: 'error' }, () => <p>Oups! An error occured</p>)
.with({ type: 'ok', data: { type: 'text' } }, (res) => <p>{res.data.content}</p>)
.with({ type: 'ok', data: { type: 'img', src: P.select() } }, (src) => <img src={src} />)
.exhaustive();Match on several values at once in a reducer
When one branch depends on both state and event, match an array of values. The Getting Started example in the README models a fetch state machine with four states and four event types, then matches [state, event] together. Use .returnType<State>() to declare the return type of the reducer.
- P.not('loading') matches any status except loading.
- P.when(predicate) adds a guard function to a property, here checking an elapsed time.
- P._ is the catch-all wildcard, used as the last clause to keep the current state.
const reducer = (state: State, event: Event) =>
match([state, event])
.returnType<State>()
.with([{ status: 'loading' }, { type: 'success' }], ([_, event]) => ({ status: 'success', data: event.data }))
.with([{ status: P.not('loading') }, { type: 'fetch' }], () => ({ status: 'loading', startTime: Date.now() }))
.with(P._, () => state)
.exhaustive();Use P.select to pull out values
P.select() marks a property so its value is passed to the handler. The README shows it used both inside a nested object and in an array pattern. Pass a name to P.select to label the value.
- Without a name, the handler receives the selected value directly.
- Selection works on nested properties, as in the Result example above.
Check data shape with isMatching and handle untyped API responses
The README lists validating the shape of data with isMatching as a feature, and it links a sandbox that handles untyped API responses. This is useful when a response arrives as unknown data and you want to confirm its structure before using it.
The source does not give detailed isMatching usage, so check the API Reference section of the full README for its exact signature.
What people use it for
- Rendering different UI for a success, error or image response from a data source.
- Writing a React reducer that branches on both the current state and the incoming event.
- Matching nested object shapes instead of chaining if/else statements.
- Validating the shape of an untyped API response before using it.
Frequently asked questions
How do I install ts-pattern?
Run npm install ts-pattern. The README also lists pnpm add ts-pattern, yarn add ts-pattern and bun add ts-pattern as alternatives.
Is ts-pattern free to use?
Yes. The repository is licensed under the MIT licence, as shown on its GitHub page and the README badge.
Does ts-pattern work with plain JavaScript?
The library is built around TypeScript type inference, so it is aimed at TypeScript projects. Plain JavaScript projects are listed as not a good fit.
What does .exhaustive() do?
It checks that you have handled every possible case, and the compiler flags cases you have not handled. This helps when you add a new variant to a union.
Is ts-pattern a good alternative to switch statements?
It can be, especially for complex conditions over tagged unions, where the README says it is shorter and more readable. For a single simple if statement, the library adds little, so a plain if is the better choice.
How big is ts-pattern?
The README describes it as only about 2kB. Teams that cannot add a runtime dependency to their bundle should consider another approach.
New agent skills, repos and AI tools — every week
The best new skills and open-source projects, the fastest-growing repos, and one free prompt. Five minutes to read. Unsubscribe anytime.
Our verdict
Use ts-pattern if you are a TypeScript developer who writes many if/else or switch branches over tagged unions, or who wants the type checker to catch missing cases. Skip it for plain JavaScript projects, for a single simple if statement, or where you cannot add a runtime dependency.
Install options & downloads