Baike.dev
All toolsTrendingOpen sourceNewsSubmit
Log in
< 返回工具列表
T

ts-pattern

> 编程语言
开源

🎨 The exhaustive Pattern Matching library for TypeScript, with smart type inference.

15.1K stars0 点赞1 次浏览
访问官网GitHub

工具介绍

🎨 The exhaustive Pattern Matching library for TypeScript, with smart type inference.

TS-Pattern

The exhaustive Pattern Matching library for TypeScript with smart type inference.

```tsx import { match, P } from 'ts-pattern'; type Data = | { type: 'text'; content: string } | { type: 'img'; src: string }; type Result = | { type: 'ok'; data: Data } | { type: 'error'; error: Error }; const result: Result = ...; const html = match(result) .with({ type: 'error' }, () =>

Oups! An error occured

) .with({ type: 'ok', data: { type: 'text' } }, (res) =>

{res.data.content}

) .with({ type: 'ok', data: { type: 'img', src: P.select() } }, (src) => ) .exhaustive(); ``` ## About Write **better** and **safer conditions**. Pattern matching lets you express complex conditions in a single, compact expression. Your code becomes **shorter** and **more readable**. Exhaustiveness checking ensures you haven’t forgotten **any possible case**.

Animation by @nicoespeon

## Features - Pattern-match on **any data structure**: nested [Objects](#objects), [Arrays](#tuples-arrays), [Tuples](#tuples-arrays), [Sets](#pset-patterns), [Maps](#pmap-patterns) and all primitive types. - **Typesafe**, with helpful [type inference](#type-inference). - **Exhaustiveness checking** support, enforcing that you are matching every possible case with [`.exhaustive()`](#exhaustive). - Use [patterns](#patterns) to **validate** the shape of your data with [`isMatching`](#ismatching). - **Expressive API**, with catch-all and type specific **wildcards**: [`P._`](#p_-wildcard), [`P.string`](#pstring-wildcard), [`P.number`](#pnumber-wildcard), etc. - Supports [**predicates**](#pwhen-patterns), [**unions**](#punion-patterns), [**intersections**](#pintersection-patterns) and [**exclusion**](#pnot-patterns) patterns for non-trivial cases. - Supports properties selection, via the [`P.select(name?)`](#pselect-patterns) function. - Tiny bundle footprint ([**only ~2kB**](https://bundlephobia.com/package/ts-pattern)). ## What is Pattern Matching? [Pattern Matching](https://en.wikipedia.org/wiki/Pattern_matching) is a code-branching technique coming from functional programming languages that's more powerful and often less verbose than imperative alternatives (if/else/switch statements), especially for complex conditions. Pattern Matching is implemented in Python, Rust, Swift, Elixir, Haskell and many other languages. There is [a tc39 proposal](https://github.com/tc39/proposal-pattern-matching) to add Pattern Matching to EcmaScript, but it is still in stage 1 and isn't likely to land before several years. Luckily, pattern matching can be implemented in userland. `ts-pattern` Provides a typesafe pattern matching implementation that you can start using today. Read the introduction blog post: [Bringing Pattern Matching to TypeScript 🎨 Introducing TS-Pattern](https://dev.to/gvergnaud/bringing-pattern-matching-to-typescript-introducing-ts-pattern-v3-0-o1k) ## Installation Via npm ```sh npm install ts-pattern ``` You can also use your favorite package manager: ```sh pnpm add ts-pattern # OR yarn add ts-pattern # OR bun add ts-pattern # OR npx jsr add @gabriel/ts-pattern ``` ## Want to become a TypeScript Expert? Check out 👉 [Type-Level TypeScript](https://type-level-typescript.com/), an online course teaching you how to unleash the full potential of TypeScript's Turing-complete type system. You already know how to code, and types are simply another programming language to master. This course **bridges the gap**, helping you apply your **existing programming knowledge** to **TypeScript's type system**, so you never again struggle with type errors or feel unable to type complex generic code correctly! # Documentation - [Sandbox examples](#sandbox-examples) - [Getting Started](#getting-started) - [API Reference](#api-reference) - [`match`](#match) - [`.with`](#with) - [`.when`](#when) - [`.returnType`](#returntype) - [`.exhaustive`](#exhaustive) - [`.otherwise`](#otherwise) - [`.narrow`](#narrow) - [`isMatching`](#ismatching) - [Patterns](#patterns) - [Literals](#literals) - [Wildcards](#wildcards) - [Objects](#objects) - [Tuples (arrays)](#tuples-arrays) - [`P.array` patterns](#parray-patterns) - [`P.record` patterns](#precord-patterns) - [`P.set`](#pset-patterns) - [`P.map`](#pmap-patterns) - [`P.when` patterns](#pwhen-patterns) - [`P.not` patterns](#pnot-patterns) - [`P.select` patterns](#pselect-patterns) - [`P.optional` patterns](#poptional-patterns) - [`P.instanceOf` patterns](#pinstanceof-patterns) - [`P.union` patterns](#punion-patterns) - [`P.intersection` patterns](#pintersection-patterns) - [`P.string` predicates](#pstring-predicates) - [`P.number` and `P.bigint` predicates](#pnumber-and-pbigint-predicates) - [Types](#types) - [`P.infer`](#pinfer) - [`P.Pattern`](#pPattern) - [Type inference](#type-inference) - [Inspirations](#inspirations) ## Sandbox examples - [Basic Demo](https://stackblitz.com/edit/vitejs-vite-qrk8po?file=src%2Fexamples%2Fbasic.tsx) - [React gif fetcher app Demo](https://stackblitz.com/edit/ts-pattern-gifs?file=src%2FApp.tsx) - [React.useReducer Demo](https://stackblitz.com/edit/ts-pattern-reducer?file=src%2FApp.tsx) - [Handling untyped API response Demo](https://stackblitz.com/edit/vitejs-vite-qrk8po?file=src%2Fexamples%2Fapi.tsx) - [`P.when` Guard Demo](https://stackblitz.com/edit/vitejs-vite-qrk8po?file=src%2Fexamples%2Fwhen.tsx) - [`P.not` Pattern Demo](https://stackblitz.com/edit/vitejs-vite-qrk8po?file=src%2Fexamples%2Fnot.tsx) - [`P.select` Pattern Demo](https://stackblitz.com/edit/vitejs-vite-qrk8po?file=src%2Fexamples%2Fselect.tsx) - [`P.union` Pattern Demo](https://stackblitz.com/edit/vitejs-vite-qrk8po?file=src%2Fexamples%2Funion.tsx) ## Getting Started As an example, let's create a state reducer for a frontend application that fetches some data. ### Example: a state reducer with ts-pattern Our application can be in four different states: `idle`, `loading`, `success` and `error`. Depending on which state we are in, some events can occur. Here are all the possible types of event our application can respond to: `fetch`, `success`, `error` and `cancel`. I use the word `event` but you can replace it with `action` if you are used to Redux's terminology. ```ts type State = | { status: 'idle' } | { status: 'loading'; startTime: number } | { status: 'success'; data: string } | { status: 'error'; error: Error }; type Event = | { type: 'fetch' } | { type: 'success'; data: string } | { type: 'error'; error: Error } | { type: 'cancel' }; ``` Even though our application can handle 4 events, **only a subset** of these events **make sense for each given state**. For instance we can only `cancel` a request if we are currently in the `loading` state. To avoid unwanted state changes that could lead to bugs, we want our state reducer function to branch on **both the state and the event**, and return a new state. This is a case where `match` really shines. Instead of writing nested switch statements, we can use pattern matching to simultaneously check the state and the event object: ``` … ``` There's a lot going on, so **let's go through this code bit by bit:** ### match(value) `match` takes a value and returns a [_builder_](https://en.wikipedia.org/wiki/Builder_pattern) on which you can add your pattern matching cases. ```ts match([state, event]) ``` It's also possible to specify the input and output type explicitly with `match(...)`, but this is usually unnecessary, as TS-Pattern is able to infer them. ### .returnType\() `.returnType` is an optional method that you can call if you want to force all following code-branches to return a value of a specific type. It takes a single type parameter, provided between ``. ```ts .returnType() ``` Here, we use this method to make sure all branches return a valid `State` object. ### .with(pattern, handler) Then we add a first `with` clause: ```ts .with( [{ status: 'loading' }, { type: 'success' }], ([state, event]) => ({ // `state` is inferred as { status: 'loading' } // `event` is inferred as { type: 'success', data: string } status: 'success', data: event.data, }) ) ``` The first argument is the **pattern**: the **shape of value** you expect for this branch. The second argument is the **handler function**: the code **branch** that will be called if the input value matches the pattern. The handler function takes the input value as first parameter with its type **narrowed down** to what the pattern matches. ### P.select(name?) In the second `with` clause, we use the `P.select` function: ```ts .with( [ { status: 'loading' }, { type: 'error', error: P.select() } ], (error) => ({ status: 'error', error }) ) ``` `P.select()` lets you **extract** a piece of your input value and **inject** it into your handler. It is pretty useful when pattern matching on deep data structures because it avoids the hassle of destructuring your input in your handler. Since we didn't pass any name to `P.select()`, It will inject the `event.error` property as first argument to the handler function. Note that you can still access **the full input value** with its type narrowed by your pattern as **second argument** of the handler function: ```ts .with( [ { status: 'loading' }, { type: 'error', error: P.select() } ], (error, stateAndEvent) => { // error: Error // stateAndEvent: [{ status: 'loading' }, { type: 'error', error: Error }] } ) ``` In a pattern, we can only have a **single** anonymous selection. If you need to select more properties on your input data structure, you will need to give them **names**: ```ts .with( [ { status: 'success', data: P.select('prevData') }, { type: 'error', error: P.select('err') } ], ({ prevData, err }) => { // Do something with (prevData: string) and (err: Error). } ) ``` Each named selection will be injected inside a `selections` object, passed as first argument to the handler function. Names can be any strings. ### P.not(pattern) If you need to match on everything **but** a specific value, you can use a `P.not()` pattern. it's a function taking a pattern and returning its opposite: ```ts .with( [{ status: P.not('loading') }, { type: 'fetch' }], () => ({ status: 'loading' }) ) ``` ### `P.when()` and guard functions Sometimes, we need to make sure our input value respects a condition that can't be expressed by a pattern. For example, imagine you need to check that a number is positive. In these cases, we can use **guard functions**: functions taking a value and returning a `boolean`. With TS-Pattern, there are two ways to use a guard function: - use `P.when()` inside one of your patterns - pass it as second parameter to `.with(...)` #### using P.when(predicate) ```ts .with( [ { status: 'loading', startTime: P.when((t) => t + 2000 < Date.now()), }, { type: 'cancel' }, ], () => ({ status: 'idle' }) ) ``` #### Passing a guard function to `.with(...)` `.with` optionally accepts a guard function as second parameter, between the `pattern` and the `handler` callback: ```ts .with( [{ status: 'loading' }, { type: 'cancel' }], ([state, event]) => state.startTime + 2000 < Date.now(), () => ({ status: 'idle' }) ) ``` This

核心特点

  • •Pattern-match on any data structure: nested Objects, Arrays, Tuples, Sets, Maps and all primitive types.
  • •Typesafe, with helpful type inference.
  • •Exhaustiveness checking support, enforcing that you are matching every possible case with .exhaustive().
  • •Use patterns to validate the shape of your data with isMatching.
  • •Expressive API, with catch-all and type specific wildcards: P._, P.string, P.number, etc.
  • •Supports predicates, unions, intersections and exclusion patterns for non-trivial cases.
  • •Supports properties selection, via the P.select(name?) function.
  • •Tiny bundle footprint (only ~2kB).
  • •Sandbox examples
  • •Getting Started

> 标签

TypeScriptbranchingconditionsexhaustiveinference

暂无评论,来聊聊你的看法吧

> 工具信息

发布日期2026年8月1日
最后更新2026年9月9日
分类编程语言
定价开源

> 相关工具

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言