ts-pattern
Advanced tools
Comparing version 4.0.1-rc.8 to 4.0.1-rc.9
{ | ||
"name": "ts-pattern", | ||
"version": "4.0.1-rc.8", | ||
"version": "4.0.1-rc.9", | ||
"description": " The exhaustive Pattern Matching library for TypeScript.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
457
README.md
@@ -21,3 +21,3 @@ <h1 align="center">ts-pattern</h1> | ||
```ts | ||
import { match, select } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -37,3 +37,3 @@ type Data = | ||
.with({ type: 'ok', data: { type: 'text' } }, (res) => `<p>${res.data.content}</p>`) | ||
.with({ type: 'ok', data: { type: 'img', src: select() } }, (src) => `<img src=${src} />`) | ||
.with({ type: 'ok', data: { type: 'img', src: P.select() } }, (src) => `<img src=${src} />`) | ||
.exhaustive(); | ||
@@ -50,7 +50,7 @@ ``` | ||
- **Typesafe**, with helpful type inference. | ||
- **Exhaustive matching** support, enforcing that you are matching every possible case with `.exhaustive()`. | ||
- **Exhaustiveness checking** support, enforcing that you are matching every possible case with `.exhaustive()`. | ||
- **Expressive API**, with catch-all and type specific **wildcards**: `__`. | ||
- Supports `when(<predicate>)` and `not(<pattern>)` patterns for complex cases. | ||
- Supports properties selection, via the `select(<name?>)` function. | ||
- Tiny bundle footprint ([**only 1.4kb**](https://bundlephobia.com/package/ts-pattern@3.2.4)). | ||
- Supports `P.when(<predicate>)` and `P.not(<pattern>)` patterns for complex cases. | ||
- Supports properties selection, via the `P.select(<name?>)` function. | ||
- Tiny bundle footprint ([**only 1.9kB**](https://bundlephobia.com/package/ts-pattern@3.2.4)). | ||
@@ -81,7 +81,7 @@ ## What is Pattern Matching? | ||
| ts-pattern | TypeScript v4.2+ | TypeScript v4.1+ | TypeScript v3.x- | | ||
| ---------- | ---------------- | ---------------- | ---------------- | | ||
| v3.x | ✅ | ⚠️ | ❌ | | ||
| v2.x | ✅ | ✅ | ❌ | | ||
| v1.x | ✅ | ✅ | ✅ | | ||
| ts-pattern | TypeScript v4.5+ | TypeScript v4.2+ | TypeScript v4.1+ | | ||
| ------------------------------------------------------- | ---------------- | ---------------- | ---------------- | | ||
| v4.x | ✅ | ❌ | ❌ | | ||
| [v3.x](https://github.com/gvergnaud/ts-pattern/tree/v3) | ✅ | ✅ | ⚠️ | | ||
| [v2.x](https://github.com/gvergnaud/ts-pattern/tree/v2) | ✅ | ✅ | ✅ | | ||
@@ -96,2 +96,4 @@ ✅ Full support | ||
Note: This is the documentation for **TS-Pattern v4**. Find the documentation for [**TS-Pattern v3 here**](https://github.com/gvergnaud/ts-pattern/tree/v3). | ||
- [Code Sandbox Examples](#code-sandbox-examples) | ||
@@ -108,16 +110,22 @@ - [Getting Started](#getting-started) | ||
- [Literals](#literals) | ||
- [`__` wildcard](#__-wildcard) | ||
- [`__.string` wildcard](#__string-wildcard) | ||
- [`__.number` wildcard](#__number-wildcard) | ||
- [`__.boolean` wildcard](#__boolean-wildcard) | ||
- [`__.nullish` wildcard](#__nullish-wildcard) | ||
- [Wildcards](#wildcards) | ||
- [`__` wildcard](#__-wildcard) | ||
- [`P.string` wildcard](#Pstring-wildcard) | ||
- [`P.number` wildcard](#Pnumber-wildcard) | ||
- [`P.boolean` wildcard](#Pboolean-wildcard) | ||
- [`P.nullish` wildcard](#Pnullish-wildcard) | ||
- [`P.bigint` wildcard](#Pbigint-wildcard) | ||
- [`P.symbol` wildcard](#Psymbol-wildcard) | ||
- [Objects](#objects) | ||
- [Lists (arrays)](#lists-arrays) | ||
- [Tuples (arrays)](#tuples-arrays) | ||
- [Sets](#sets) | ||
- [Maps](#maps) | ||
- [`when` guards](#when-guards) | ||
- [`not` patterns](#not-patterns) | ||
- [`select` patterns](#select-patterns) | ||
- [`instanceOf` patterns](#instanceof-patterns) | ||
- [`P.array` patterns](#Parray-patterns) | ||
- [`P.when` patterns](#Pwhen-patterns) | ||
- [`P.not` patterns](#Pnot-patterns) | ||
- [`P.select` patterns](#Pselect-patterns) | ||
- [`P.optional` patterns](#Poptional-patterns) | ||
- [`P.union` patterns](#Punion-patterns) | ||
- [`P.intersection` patterns](#Pintersection-patterns) | ||
- [`P.instanceOf` patterns](#Pinstanceof-patterns) | ||
- [Type inference](#type-inference) | ||
@@ -128,9 +136,12 @@ - [Inspirations](#inspirations) | ||
- [Basic Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/basic.tsx) | ||
- [Gif fetcher app Demo (with React)](https://codesandbox.io/s/ts-pattern-gif-search-demo-n8h4k?file=/src/App.tsx) | ||
- [Reducer Demo (with React)](https://codesandbox.io/s/ts-pattern-reducer-example-c4yuq?file=/src/App.tsx) | ||
- [Untyped Input Demo (Handling an API response)](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/api.tsx) | ||
- [`when` Guard Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/when.tsx) | ||
- [`not` Pattern Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/not.tsx) | ||
- [`select` Pattern Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/select.tsx) | ||
- TS-Pattern v4 | ||
- Unfortunatelly Codesandbox doesn't support TypeScript v4.5 yet, so v4 demos aren't available. | ||
- [TS-Pattern v3](<(https://github.com/gvergnaud/ts-pattern/tree/v3)>) | ||
- [Basic Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/basic.tsx) | ||
- [React gif fetcher app Demo](https://codesandbox.io/s/ts-pattern-gif-search-demo-n8h4k?file=/src/App.tsx) | ||
- [React.useReducer Demo](https://codesandbox.io/s/ts-pattern-reducer-example-c4yuq?file=/src/App.tsx) | ||
- [Handling untyped API response Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/api.tsx) | ||
- [`when` Guard Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/when.tsx) | ||
- [`not` Pattern Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/not.tsx) | ||
- [`select` Pattern Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/select.tsx) | ||
@@ -177,3 +188,3 @@ ## Getting Started | ||
```ts | ||
import { match, __, not, select, when } from 'ts-pattern'; | ||
import { match, P, __ } from 'ts-pattern'; | ||
@@ -188,3 +199,3 @@ const reducer = (state: State, event: Event): State => | ||
.with( | ||
[{ status: 'loading' }, { type: 'error', error: select() }], | ||
[{ status: 'loading' }, { type: 'error', error: P.select() }], | ||
(error) => ({ | ||
@@ -196,3 +207,3 @@ status: 'error', | ||
.with([{ status: not('loading') }, { type: 'fetch' }], () => ({ | ||
.with([{ status: P.not('loading') }, { type: 'fetch' }], () => ({ | ||
status: 'loading', | ||
@@ -204,3 +215,3 @@ startTime: Date.now(), | ||
[ | ||
{ status: 'loading', startTime: when((t) => t + 2000 < Date.now()) }, | ||
{ status: 'loading', startTime: P.when((t) => t + 2000 < Date.now()) }, | ||
{ type: 'cancel' }, | ||
@@ -258,9 +269,9 @@ ], | ||
### select(name?) | ||
### P.select(name?) | ||
In the second `with` clause, we use the `select` function: | ||
In the second `with` clause, we use the `P.select` function: | ||
```ts | ||
.with( | ||
[{ status: 'loading' }, { type: 'error', error: select() }], | ||
[{ status: 'loading' }, { type: 'error', error: P.select() }], | ||
(error) => ({ | ||
@@ -273,9 +284,9 @@ status: 'error', | ||
`select` let 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. | ||
`P.select()` let 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 `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: | ||
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: select() }], | ||
[{ status: 'loading' }, { type: 'error', error: P.select() }], | ||
(error, stateAndEvent) => { | ||
@@ -292,3 +303,3 @@ // error: Error | ||
.with( | ||
[{ status: 'success', data: select('prevData') }, { type: 'error', error: select('err') }], | ||
[{ status: 'success', data: P.select('prevData') }, { type: 'error', error: P.select('err') }], | ||
({ prevData, err }) => { | ||
@@ -302,8 +313,8 @@ // Do something with (prevData: string) and (err: Error). | ||
### not(pattern) | ||
### P.not(pattern) | ||
If you need to match on everything **but** a specific value, you can use a `not(<pattern>)` pattern. it's a function taking a pattern and returning its opposite: | ||
If you need to match on everything **but** a specific value, you can use a `P.not(<pattern>)` pattern. it's a function taking a pattern and returning its opposite: | ||
```ts | ||
.with([{ status: not('loading') }, { type: 'fetch' }], () => ({ | ||
.with([{ status: P.not('loading') }, { type: 'fetch' }], () => ({ | ||
status: 'loading', | ||
@@ -313,3 +324,3 @@ })) | ||
### `when()` and guard functions | ||
### `P.when()` and guard functions | ||
@@ -320,6 +331,6 @@ 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 if a number is positive. In these cases, we can use **guard functions**: functions taking a value and returning a `boolean`. | ||
- use `when(<guard function>)` inside your pattern | ||
- use `P.when(<guard function>)` inside your pattern | ||
- pass it as second parameter to `.with(...)` | ||
#### using when(predicate) | ||
#### using P.when(predicate) | ||
@@ -331,3 +342,3 @@ ```ts | ||
status: 'loading', | ||
startTime: when((t) => t + 2000 < Date.now()), | ||
startTime: P.when((t) => t + 2000 < Date.now()), | ||
}, | ||
@@ -375,2 +386,4 @@ { type: 'cancel' }, | ||
You can also use `P.__`, which is an alias to `__`. | ||
### .exhaustive(), .otherwise() and .run() | ||
@@ -597,7 +610,7 @@ | ||
```ts | ||
import { isMatching, __ } from 'ts-pattern'; | ||
import { isMatching, P } from 'ts-pattern'; | ||
const isBlogPost = isMatching({ | ||
title: __.string, | ||
description: __.string, | ||
title: P.string, | ||
description: P.string, | ||
}); | ||
@@ -614,4 +627,4 @@ | ||
const blogPostPattern = { | ||
title: __.string, | ||
description: __.string, | ||
title: P.string, | ||
description: P.string, | ||
}; | ||
@@ -680,4 +693,8 @@ | ||
#### `__` wildcard | ||
- [Wildcards](#wildcards) | ||
#### Wildcards | ||
##### `__` wildcard | ||
The `__` pattern will match any value. | ||
@@ -698,8 +715,8 @@ | ||
#### `__.string` wildcard | ||
##### `P.string` wildcard | ||
The `__.string` pattern will match any value of type `string`. | ||
The `P.string` pattern will match any value of type `string`. | ||
```ts | ||
import { match, __ } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -710,4 +727,4 @@ const input = 'hello'; | ||
.with('bonjour', () => 'Won‘t match') | ||
.with(__.string, () => 'it is a string!') | ||
.run(); | ||
.with(P.string, () => 'it is a string!') | ||
.exhaustive(); | ||
@@ -718,8 +735,8 @@ console.log(output); | ||
#### `__.number` wildcard | ||
##### `P.number` wildcard | ||
The `__.number` pattern will match any value of type `number`. | ||
The `P.number` pattern will match any value of type `number`. | ||
```ts | ||
import { match, __ } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -729,5 +746,5 @@ const input = 2; | ||
const output = match<number | string>(input) | ||
.with(__.string, () => 'it is a string!') | ||
.with(__.number, () => 'it is a number!') | ||
.run(); | ||
.with(P.string, () => 'it is a string!') | ||
.with(P.number, () => 'it is a number!') | ||
.exhaustive(); | ||
@@ -738,8 +755,8 @@ console.log(output); | ||
#### `__.boolean` wildcard | ||
##### `P.boolean` wildcard | ||
The `__.boolean` pattern will match any value of type `boolean`. | ||
The `P.boolean` pattern will match any value of type `boolean`. | ||
```ts | ||
import { match, __ } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -749,6 +766,6 @@ const input = true; | ||
const output = match<number | string | boolean>(input) | ||
.with(__.string, () => 'it is a string!') | ||
.with(__.number, () => 'it is a number!') | ||
.with(__.boolean, () => 'it is a boolean!') | ||
.run(); | ||
.with(P.string, () => 'it is a string!') | ||
.with(P.number, () => 'it is a number!') | ||
.with(P.boolean, () => 'it is a boolean!') | ||
.exhaustive(); | ||
@@ -759,5 +776,5 @@ console.log(output); | ||
#### `__.nullish` wildcard | ||
##### `P.nullish` wildcard | ||
The `__.nullish` pattern will match any value of type `null` or `undefined`. | ||
The `P.nullish` pattern will match any value of type `null` or `undefined`. | ||
@@ -771,3 +788,3 @@ You will **not often need this wildcard** as ordinarily `null` and `undefined` | ||
```ts | ||
import { match, __ } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -777,6 +794,6 @@ const input = null; | ||
const output = match<number | string | boolean | null | undefined>(input) | ||
.with(__.string, () => 'it is a string!') | ||
.with(__.number, () => 'it is a number!') | ||
.with(__.boolean, () => 'it is a boolean!') | ||
.with(__.nullish, () => 'it is either null or undefined!') | ||
.with(P.string, () => 'it is a string!') | ||
.with(P.number, () => 'it is a number!') | ||
.with(P.boolean, () => 'it is a boolean!') | ||
.with(P.nullish, () => 'it is either null or undefined!') | ||
.with(null, () => 'it is null!') | ||
@@ -790,2 +807,36 @@ .with(undefined, () => 'it is undefined!') | ||
##### `P.bigint` wildcard | ||
The `P.bigint` pattern will match any value of type `bigint`. | ||
```ts | ||
import { match, P } from 'ts-pattern'; | ||
const input = 20000000n; | ||
const output = match<bigint | null>(input) | ||
.with(P.bigint, () => 'it is a bigint!') | ||
.otherwise(() => '?'); | ||
console.log(output); | ||
// => 'it is a bigint!' | ||
``` | ||
##### `P.symbol` wildcard | ||
The `P.symbol` pattern will match any value of type `symbol`. | ||
```ts | ||
import { match, P } from 'ts-pattern'; | ||
const input = Symbol('some symbol'); | ||
const output = match<symbol | null>(input) | ||
.with(P.symbol, () => 'it is a symbol!') | ||
.otherwise(() => '?'); | ||
console.log(output); | ||
// => 'it is a symbol!' | ||
``` | ||
#### Objects | ||
@@ -817,29 +868,2 @@ | ||
#### Lists (arrays) | ||
To match on a list of values, your pattern can be an array with a single sub-pattern in it. | ||
This sub-pattern will be tested against all elements in your input array, and they | ||
must all match for your list pattern to match. | ||
```ts | ||
import { match, __ } from 'ts-pattern'; | ||
type Input = { title: string; content: string }[]; | ||
let input: Input = [ | ||
{ title: 'Hello world!', content: 'This is a very interesting content' }, | ||
{ title: 'Bonjour!', content: 'This is a very interesting content too' }, | ||
]; | ||
const output = match(input) | ||
.with( | ||
[{ title: __.string, content: __.string }], | ||
(posts) => 'a list of posts!' | ||
) | ||
.otherwise(() => 'something else'); | ||
console.log(output); | ||
// => 'a list of posts!' | ||
``` | ||
#### Tuples (arrays) | ||
@@ -879,3 +903,3 @@ | ||
```ts | ||
import { match, __ } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -889,4 +913,4 @@ type Input = Set<string | number>; | ||
.with(new Set([1, 2]), (set) => `Set contains 1 and 2`) | ||
.with(new Set([__.string]), (set) => `Set contains only strings`) | ||
.with(new Set([__.number]), (set) => `Set contains only numbers`) | ||
.with(new Set([P.string]), (set) => `Set contains only strings`) | ||
.with(new Set([P.number]), (set) => `Set contains only numbers`) | ||
.otherwise(() => ''); | ||
@@ -910,3 +934,3 @@ | ||
```ts | ||
import { match, __ } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -923,7 +947,7 @@ type Input = Map<string, string | number>; | ||
.with(new Map([['b', 2]]), (map) => `map.get('b') is 2`) | ||
.with(new Map([['a', __.string]]), (map) => `map.get('a') is a string`) | ||
.with(new Map([['a', P.string]]), (map) => `map.get('a') is a string`) | ||
.with( | ||
new Map([ | ||
['a', __.number], | ||
['c', __.number], | ||
['a', P.number], | ||
['c', P.number], | ||
]), | ||
@@ -938,12 +962,39 @@ (map) => `map.get('a') and map.get('c') are number` | ||
#### `when` guards | ||
#### `P.array` patterns | ||
the `when` function enables you to test the input with a custom guard function. | ||
The pattern will match only if all `when` functions return a truthy value. | ||
To match on an array of unknown size, you can use `P.array(subpattern)`. | ||
It takes a sub-pattern, and returns a pattern which will match if all | ||
elements in the input array, match the sub-pattern. | ||
```ts | ||
import { match, P } from 'ts-pattern'; | ||
type Input = { title: string; content: string }[]; | ||
let input: Input = [ | ||
{ title: 'Hello world!', content: 'This is a very interesting content' }, | ||
{ title: 'Bonjour!', content: 'This is a very interesting content too' }, | ||
]; | ||
const output = match(input) | ||
.with( | ||
P.array({ title: P.string, content: P.string }), | ||
(posts) => 'a list of posts!' | ||
) | ||
.otherwise(() => 'something else'); | ||
console.log(output); | ||
// => 'a list of posts!' | ||
``` | ||
#### `P.when` patterns | ||
the `P.when` function enables you to test the input with a custom guard function. | ||
The pattern will match only if all `P.when` functions return a truthy value. | ||
Note that you can narrow down the type of your input by providing a | ||
[Type Guard function](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) to when. | ||
[Type Guard function](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) to `P.when`. | ||
```ts | ||
import { match, when } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -955,8 +1006,8 @@ type Input = { score: number }; | ||
{ | ||
score: when((score): score is 5 => score === 5), | ||
score: P.when((score): score is 5 => score === 5), | ||
}, | ||
(input) => '😐' // input is infered as { score: 5 } | ||
) | ||
.with({ score: when((score) => score < 5) }, () => '😞') | ||
.with({ score: when((score) => score > 5) }, () => '🙂') | ||
.with({ score: P.when((score) => score < 5) }, () => '😞') | ||
.with({ score: P.when((score) => score > 5) }, () => '🙂') | ||
.run(); | ||
@@ -968,9 +1019,9 @@ | ||
#### `not` patterns | ||
#### `P.not` patterns | ||
The `not` function enables you to match on everything **but** a specific value. | ||
The `P.not` function enables you to match on everything **but** a specific value. | ||
it's a function taking a pattern and returning its opposite: | ||
```ts | ||
import { match, not } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -981,3 +1032,3 @@ type Input = boolean | number; | ||
match(input) | ||
.with(not(__.boolean), (n) => n) // n: number | ||
.with(P.not(P.boolean), (n) => n) // n: number | ||
.with(true, () => 1) | ||
@@ -993,5 +1044,5 @@ .with(false, () => 0) | ||
#### `select` patterns | ||
#### `P.select` patterns | ||
The `select` function enables us to pick a piece of our input data structure | ||
The `P.select` function enables us to pick a piece of our input data structure | ||
and inject it in our handler function. | ||
@@ -1002,3 +1053,3 @@ | ||
Selections can be either named (with `select('someName')`) or anonymous (with `select()`). | ||
Selections can be either named (with `P.select('someName')`) or anonymous (with `P.select()`). | ||
@@ -1008,3 +1059,3 @@ You can have only one anonymous selection by pattern, and the selected value will be directly inject in your handler as first argument: | ||
```ts | ||
import { match, select } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -1019,3 +1070,3 @@ type Input = | ||
.with( | ||
{ type: 'post', user: { name: select() } }, | ||
{ type: 'post', user: { name: P.select() } }, | ||
username => username // username: string | ||
@@ -1029,6 +1080,6 @@ ) | ||
If you need to select several things inside your input data structure, you can name your selections by giving a string to `select(<name>)`. Each selection will be passed as first argument to your handler in an object. | ||
If you need to select several things inside your input data structure, you can name your selections by giving a string to `P.select(<name>)`. Each selection will be passed as first argument to your handler in an object. | ||
```ts | ||
import { match, select } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -1043,3 +1094,3 @@ type Input = | ||
.with( | ||
{ type: 'post', user: { name: select('name') }, content: select('body') }, | ||
{ type: 'post', user: { name: P.select('name') }, content: P.select('body') }, | ||
({ name, body }) => `${name} wrote "${body}"` | ||
@@ -1053,9 +1104,105 @@ ) | ||
#### `instanceOf` patterns | ||
You can also pass a sub-pattern to `P.select` if you want it to only | ||
select values which match this sub-pattern: | ||
The `instanceOf` function lets you build a pattern to check if | ||
```ts | ||
type User = { age: number; name: string }; | ||
type Post = { body: string }; | ||
type Input = { author: User; content: Post }; | ||
const output = match<Input>(input) | ||
.with( | ||
{ | ||
author: P.select({ age: P.when((age) => age > 18) }), | ||
}, | ||
(author) => author // author: User | ||
) | ||
.with( | ||
{ | ||
author: P.select('author', { age: P.when((age) => age > 18) }), | ||
content: P.select(), | ||
}, | ||
({ author, content }) => author // author: User, content: Post | ||
) | ||
.otherwise(() => 'anonymous'); | ||
``` | ||
#### `P.optional` patterns | ||
`P.optional(subpattern)` let you annotate a key in an object pattern as being optional, | ||
but if it is defined it should match a given sub-pattern. | ||
```ts | ||
import { match, P } from 'ts-pattern'; | ||
type Input = { key?: string | number }; | ||
const output = match(input) | ||
.with({ key: P.optional(P.string) }, (a) => { | ||
return a.key; // string | undefined | ||
}) | ||
.with({ key: P.optional(P.number) }, (a) => { | ||
return a.key; // number | undefined | ||
}) | ||
.exhaustive(); | ||
``` | ||
#### `P.union` patterns | ||
`P.union(...subpatterns)` let you test several patterns and will match if | ||
one of these patterns do. It's particularly handy when you want to handle | ||
some cases of a union type in the same code branch: | ||
```ts | ||
import { match, P } from 'ts-pattern'; | ||
type Input = | ||
| { type: 'user'; name: string } | ||
| { type: 'org'; name: string } | ||
| { type: 'text'; content: string } | ||
| { type: 'img'; src: string }; | ||
const output = match<Input>(input) | ||
.with({ type: P.union('user', 'org') }, (userOrOrg) => { | ||
// userOrOrg: User | Org | ||
return userOrOrg.name; | ||
}) | ||
.otherwise(() => ''); | ||
``` | ||
#### `P.intersection` patterns | ||
`P.intersection(...subpatterns)` let you ensure that the input matches | ||
**all** sub-patterns passed as parameters. | ||
```ts | ||
class A { | ||
constructor(public foo: 'bar' | 'baz') {} | ||
} | ||
class B { | ||
constructor(public str: string) {} | ||
} | ||
type Input = { prop: A | B }; | ||
const output = match<Input>(input) | ||
.with( | ||
{ prop: P.intersection(P.instanceOf(A), { foo: 'bar' }) }, | ||
({ prop }) => prop.foo // prop: A & { foo: 'bar' } | ||
) | ||
.with( | ||
{ prop: P.intersection(P.instanceOf(A), { foo: 'baz' }) }, | ||
({ prop }) => prop.foo // prop: A & { foo: 'baz' } | ||
) | ||
.otherwise(() => ''); | ||
``` | ||
#### `P.instanceOf` patterns | ||
The `P.instanceOf` function lets you build a pattern to check if | ||
a value is an instance of a class: | ||
```ts | ||
import { match, instanceOf } from 'ts-pattern'; | ||
import { match, P } from 'ts-pattern'; | ||
@@ -1074,6 +1221,6 @@ class A { | ||
const output = match<Input>(input) | ||
.with({ value: instanceOf(A) }, (a) => { | ||
.with({ value: P.instanceOf(A) }, (a) => { | ||
return 'instance of A!'; | ||
}) | ||
.with({ value: instanceOf(B) }, (b) => { | ||
.with({ value: P.instanceOf(B) }, (b) => { | ||
return 'instance of B!'; | ||
@@ -1096,22 +1243,22 @@ }) | ||
.with(__, (value) => 'ok') // value: Input | ||
.with(__.string, (value) => 'ok') // value: string | ||
.with(P.string, (value) => 'ok') // value: string | ||
.with( | ||
when((value) => true), | ||
P.when((value) => true), | ||
(value) => 'ok' // value: Input | ||
) | ||
.with( | ||
when((value): value is string => true), | ||
P.when((value): value is string => true), | ||
(value) => 'ok' // value: string | ||
) | ||
.with(not('hello'), (value) => 'ok') // value: Input | ||
.with(not(__.string), (value) => 'ok') // value: { type: string } | ||
.with(not({ type: __.string }), (value) => 'ok') // value: string | ||
.with(not(when(() => true)), (value) => 'ok') // value: Input | ||
.with(P.not('hello'), (value) => 'ok') // value: Input | ||
.with(P.not(P.string), (value) => 'ok') // value: { type: string } | ||
.with(P.not({ type: P.string }), (value) => 'ok') // value: string | ||
.with(P.not(P.when(() => true)), (value) => 'ok') // value: Input | ||
.with({ type: __ }, (value) => 'ok') // value: { type: string } | ||
.with({ type: __.string }, (value) => 'ok') // value: { type: string } | ||
.with({ type: when(() => true) }, (value) => 'ok') // value: { type: string } | ||
.with({ type: not('hello' as const) }, (value) => 'ok') // value: { type: string } | ||
.with({ type: not(__.string) }, (value) => 'ok') // value: never | ||
.with({ type: not(when(() => true)) }, (value) => 'ok') // value: { type: string } | ||
.run(); | ||
.with({ type: P.string }, (value) => 'ok') // value: { type: string } | ||
.with({ type: P.when(() => true) }, (value) => 'ok') // value: { type: string } | ||
.with({ type: P.not('hello' as const) }, (value) => 'ok') // value: { type: string } | ||
.with({ type: P.not(P.string) }, (value) => 'ok') // value: never | ||
.with({ type: P.not(P.when(() => true)) }, (value) => 'ok') // value: { type: string } | ||
.exhaustive(); | ||
``` | ||
@@ -1125,13 +1272,1 @@ | ||
to wait for it to be added to the language itself. I'm really grateful for that 🙏 | ||
#### how is this different from `typescript-pattern-matching` | ||
Wim Jongeneel released his own npm package for pattern matching. `ts-pattern` has a few | ||
notable differences: | ||
- `ts-patterns`'s goal is to be a well unit-tested, well documented, production ready library. | ||
- It supports more data structures, like tuples, sets and maps. | ||
- It provides a "catch all" pattern: `__`. | ||
- It supports exhaustive matching with `.exhaustive()`. | ||
- It supports deep selection with the `select()` function. | ||
- Its type inference works on deeper patterns and is well tested. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
103646
37
1426
1222
1