ts-pattern
Advanced tools
Comparing version 0.3.1 to 0.3.2
@@ -1,2 +0,2 @@ | ||
import { PatternType, __ } from './Pattern'; | ||
import { PatternType, __, Primitives } from './Pattern'; | ||
import { ExcludeIfContainsNever, LeastUpperBound } from './helpers'; | ||
@@ -6,4 +6,4 @@ export declare type ExtractPreciseValue<a, b> = ExcludeIfContainsNever<b extends [] ? [] : b extends typeof __ ? a : b extends { | ||
value: infer b1; | ||
} ? Exclude<a, b1> : [a, b] extends [([infer a1, infer a2, infer a3, infer a4, infer a5] | infer otherBranches), [infer b1, infer b2, infer b3, infer b4, infer b5]] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>, ExtractPreciseValue<a3, b3>, ExtractPreciseValue<a4, b4>, ExtractPreciseValue<a5, b5>] : [a, b] extends [[infer a1, infer a2, infer a3, infer a4] | infer otherBranches, [infer b1, infer b2, infer b3, infer b4]] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>, ExtractPreciseValue<a3, b3>, ExtractPreciseValue<a4, b4>] : [a, b] extends [[infer a1, infer a2, infer a3] | infer otherBranches, [infer b1, infer b2, infer b3]] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>, ExtractPreciseValue<a3, b3>] : [a, b] extends [[infer a1, infer a2] | infer otherBranches, [infer b1, infer b2]] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>] : [a, b] extends [(infer a1)[], (infer b1)[]] ? ExtractPreciseValue<a1, b1>[] : [a, b] extends [Map<infer ak, infer av>, Map<infer bk, infer bv>] ? Map<ExtractPreciseValue<ak, bk>, ExtractPreciseValue<av, bv>> : [a, b] extends [Set<infer av>, Set<infer bv>] ? Set<ExtractPreciseValue<av, bv>> : b extends object ? a extends object ? b extends a ? b : a extends b ? a : { | ||
} ? Exclude<a, b1> : b extends [infer b1, infer b2, infer b3, infer b4, infer b5] ? a extends [infer a1, infer a2, infer a3, infer a4, infer a5] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>, ExtractPreciseValue<a3, b3>, ExtractPreciseValue<a4, b4>, ExtractPreciseValue<a5, b5>] : never : b extends [infer b1, infer b2, infer b3, infer b4] ? a extends [infer a1, infer a2, infer a3, infer a4] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>, ExtractPreciseValue<a3, b3>, ExtractPreciseValue<a4, b4>] : never : b extends [infer b1, infer b2, infer b3] ? a extends [infer a1, infer a2, infer a3] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>, ExtractPreciseValue<a3, b3>] : never : b extends [infer b1, infer b2] ? a extends [infer a1, infer a2] ? [ExtractPreciseValue<a1, b1>, ExtractPreciseValue<a2, b2>] : never : b extends (infer b1)[] ? a extends (infer a1)[] ? ExtractPreciseValue<a1, b1>[] : never : b extends Map<infer bk, infer bv> ? a extends Map<infer ak, infer av> ? Map<ExtractPreciseValue<ak, bk>, ExtractPreciseValue<av, bv>> : never : b extends Set<infer bv> ? a extends Set<infer av> ? Set<ExtractPreciseValue<av, bv>> : never : b extends object ? a extends any[] | Set<any> | Map<any, any> | Primitives ? never : b extends a ? b : a extends b ? a : { | ||
[k in keyof Required<a>]: k extends keyof b ? ExtractPreciseValue<a[k], b[k]> : a[k]; | ||
} : never : LeastUpperBound<a, b>>; | ||
} : LeastUpperBound<a, b>>; |
@@ -15,5 +15,8 @@ export declare type ValueOf<a> = a[keyof a]; | ||
**/ | ||
export declare type ExcludeIfContainsNever<a> = a extends any[] | Map<any, any> | Set<any> ? a : a extends object ? { | ||
export declare type ExcludeIfContainsNever<a> = a extends Map<any, any> | Set<any> ? a : a extends any[] ? ExcludeNeverArray<a> : a extends object ? ExcludeNeverObject<a> : a; | ||
declare type ExcludeNeverArray<a extends any[]> = (a[0] extends never ? false : true) | (a[1] extends never ? false : true) | (a[2] extends never ? false : true) | (a[3] extends never ? false : true) | (a[4] extends never ? false : true) extends true ? a : never; | ||
declare type ExcludeNeverObject<a extends object> = { | ||
[k in keyof a]-?: a[k] extends never ? 'exclude' : 'include'; | ||
}[keyof a] extends 'include' ? a : never : a; | ||
}[keyof a] extends 'include' ? a : never; | ||
export declare type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; | ||
export {}; |
@@ -42,3 +42,3 @@ import { Pattern, SelectPattern, GuardValue } from './Pattern'; | ||
* ### Match | ||
* An interface to create a pattern matching close. | ||
* An interface to create a pattern matching clause. | ||
*/ | ||
@@ -45,0 +45,0 @@ export declare type Match<a, b> = { |
{ | ||
"name": "ts-pattern", | ||
"version": "0.3.1", | ||
"version": "0.3.2", | ||
"description": "Typescript pattern matching library", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
272
README.md
# TS Pattern | ||
The Pattern Matching library for TypeScript you have been missing. | ||
A complete Pattern Matching library for [TypeScript](https://github.com/microsoft/TypeScript) | ||
with great type inference. | ||
## What is Pattern Matching? | ||
Pattern Matching is a technique coming from Functional Programming languages to declaratively write conditional code branches based on the structure of one or several values. It is a well proven technique much more powerful and much less verbose than imperative alternatives (if/else/switch statements) especially when branching on several values. | ||
Pattern Matching is a technique coming from Functional Programming languages to declaratively write conditional code branches based on the structure of one or several values. It is a well proven technique, much more powerful and much less verbose than imperative alternatives (if/else/switch statements) especially when branching on complex data structures or on several values. | ||
@@ -13,17 +14,41 @@ Pattern Matching is implemented in Elixir, Rust, Haskell, Swift and many other languages. There is [a tc39 proposal](https://github.com/tc39/proposal-pattern-matching) to add Pattern Matching to the EcmaScript specification, but it is still in stage 1 and isn't likely to land before several years (if ever). Lukily, pattern matching can be implemented in userland. `ts-pattern` Provides a typesafe pattern matching implementation that you can start using today. | ||
- Supports every data structure you use: objects, arrays, tuples, Sets, Maps, and all primitive types. | ||
- Typesafe, with great type inference. | ||
- Catch all (`__`) and type specific wild cards support. | ||
- Supports `when(predicate)` and `not(pattern)` patterns for complexe cases. | ||
- Supports properties selection, via the `select(name)` function. | ||
- Tiny bundle footprint (1kb). | ||
- Supports **every data structure** you use: objects, arrays, tuples, Sets, Maps, and all primitive types. | ||
- **Typesafe**, with great type inference. | ||
- Supports catch all (`__`) and type specific **wildcards**. | ||
- Supports `when(<predicate>)` and `not(<pattern>)` patterns for complexe cases. | ||
- Supports properties selection, via the `select(<name>)` function. | ||
- Tiny bundle footprint (**only 1kb**). | ||
## Gist | ||
## Installation | ||
Sometimes you want to match on two values at once. Here we pattern match | ||
on both the current state and the event (or action) and return a new state. | ||
Via npm: | ||
``` | ||
npm install ts-pattern | ||
``` | ||
Via yarn | ||
``` | ||
yarn add ts-pattern | ||
``` | ||
## Example and intro to key concepts | ||
Sometimes you want to match on two values at once. Let's say we want to | ||
create a reducer function, we could make a switch on the event's type, but | ||
generally an event only makes sense if we are in a certain state. | ||
To avoid unwanted state changes that could lead to bugs, **we pattern match | ||
on both the state and the event** and return a new state. | ||
I use the word `event` but you can replace it with `action` if you are used | ||
to Redux's terminology. | ||
### Full example | ||
```ts | ||
type State = | ||
| { status: 'loading' } | ||
| { status: 'idle' } | ||
| { status: 'loading'; startTime: number } | ||
| { status: 'success'; data: string } | ||
@@ -35,17 +60,11 @@ | { status: 'error'; error: Error }; | ||
| { type: 'success'; data: string } | ||
| { type: 'error'; error: Error }; | ||
| { type: 'error'; error: Error } | ||
| { type: 'cancel' }; | ||
``` | ||
```ts | ||
import { match, __, not } from 'ts-pattern'; | ||
import { match, __, not, select, when } from 'ts-pattern'; | ||
const initState: State = { | ||
status: 'idle', | ||
}; | ||
const reducer = (state: State, event: Event): State => | ||
// | ||
match<[State, Event], State>([state, event]) | ||
// the first argument is the pattern : the shape of the value | ||
// you expect for this branch. | ||
.with([{ status: 'loading' }, { type: 'success' }], ([, event]) => ({ | ||
@@ -55,50 +74,207 @@ status: 'success', | ||
})) | ||
// The second argument is the function that will be called if | ||
// the data matches the given pattern. | ||
// The type of the data structure is narrowed down to | ||
// what is permitted by the pattern. | ||
.with([{ status: 'loading' }, { type: 'error' }], ([, event]) => ({ | ||
status: 'error', | ||
error: event.error, | ||
})) | ||
// if you need to exclude a value, you can use | ||
// a `not` pattern. it's a function taking a pattern | ||
// and returning its opposite. | ||
.with( | ||
[{ status: 'loading' }, { type: 'error', error: select('err') }], | ||
(_, { err }) => ({ | ||
status: 'error', | ||
error: err, | ||
}) | ||
) | ||
.with([{ status: not('loading') }, { type: 'fetch' }], () => ({ | ||
status: 'loading', | ||
startTime: Date.now(), | ||
})) | ||
// `__` is a wildcard, it will match any value. | ||
// You can use it at the top level, or inside a data structure. | ||
.with( | ||
[ | ||
{ | ||
status: 'loading', | ||
startTime: when((startTime) => Date.now() > startTime + 1000), | ||
}, | ||
{ type: 'cancel' }, | ||
], | ||
() => ({ | ||
status: 'idle', | ||
}) | ||
) | ||
.with(__, () => state) | ||
// `run` execute the match close, and returns the value | ||
// .run(); | ||
// You can also use `otherwise`, which take an handler returning | ||
// a default value. It is equivalent to `with(__, handler).run()`. | ||
.otherwise(() => state); | ||
.run(); | ||
``` | ||
**Let's go through this bit by bit:** | ||
### match(value) | ||
`match` takes a value and returns a builder on which you can | ||
add your pattern matching cases. | ||
```ts | ||
match<[State, Event], State>([state, event]); | ||
``` | ||
Here we wrap the state and the event objects in an array and we explicitly | ||
specify the type `[State, Event]` to make sure it is interpreted as | ||
a [Tuple](https://en.wikipedia.org/wiki/Tuple) by TypeScript, so we | ||
can match on each value separately. | ||
Most of the time, you don't need to specify the type of input | ||
and output with `match<Input, Output>(...)` because `match` is able to | ||
infer both of this values. | ||
### .with(pattern, handler) | ||
Then we add a first `with` clause: | ||
```ts | ||
.with([{ status: 'loading' }, { type: 'success' }], ([state, event]) => ({ | ||
// `state` is infered as { status: 'loading' } | ||
// `event` is infered 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 **branch** that will be called if | ||
the data matches the given pattern. | ||
The **type** of the data structure is **narrowed down** to | ||
what is permitted by the pattern. | ||
### select(name) | ||
In the second `with` clause, we use the `select` function: | ||
```ts | ||
.with( | ||
[{ status: 'loading' }, { type: 'error', error: select('err') }], | ||
(_, { err }) => ({ | ||
status: 'error', | ||
error: err, | ||
}) | ||
) | ||
``` | ||
It will inject the `event.error` property inside a `selections` object given as | ||
second argument to the handler function. the `select` function takes the **name** of | ||
the selection, which can be whatever you like. | ||
It is pretty useful when pattern matching on deep data structures because it avoids | ||
the hassle of destructuring it in your handler. | ||
### 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: | ||
```ts | ||
.with([{ status: not('loading') }, { type: 'fetch' }], () => ({ | ||
status: 'loading', | ||
})) | ||
``` | ||
### when(predicate) | ||
The `when` function enables you to **add a guard** to your pattern. | ||
Your pattern will not match **unless your predicate returns `true`**. | ||
It might be handy if you need to make a dynamic checks on | ||
your data structure. | ||
```ts | ||
.with( | ||
[ | ||
{ | ||
status: 'loading', | ||
startTime: when((startTime) => Date.now() > startTime + 1000), | ||
}, | ||
{ type: 'cancel' }, | ||
], | ||
() => ({ | ||
status: 'idle', | ||
}) | ||
) | ||
``` | ||
### the `__` wildcard | ||
`__` will match any value. | ||
You can use it at the top level, or inside your pattern. | ||
```ts | ||
.with(__, () => state) | ||
// You could also use it inside your pattern: | ||
.with([__, __], () => state) | ||
// at any level: | ||
.with([__, { type: __ }], () => state) | ||
``` | ||
### run() and otherwise() | ||
```ts | ||
.run(); | ||
``` | ||
`run()` execute the pattern matching, and **returns the result**. | ||
Alternatively you can use `otherwise`, which take an handler returning | ||
a default value. `.otherwise(handler)` is equivalent to `.with(__, handler).run()`. | ||
```ts | ||
.otherwise(() => state); | ||
``` | ||
## Code Sandbox Examples | ||
- Simple example | ||
- Basic | ||
- [Reducer Demo (in React)](https://codesandbox.io/s/ts-pattern-reducer-example-c4yuq?file=/src/App.tsx) | ||
- [`when` guards Demo](https://codesandbox.io/s/ts-pattern-when-guard-example-0s6d8?file=/src/index.ts) | ||
- Polymorphic input | ||
- Untyped input (e.g. an API response) | ||
- [`when` guards Demo](https://codesandbox.io/s/ts-pattern-examples-0s6d8?file=/src/examples/when.ts) | ||
- `not` patterns | ||
- `select` pattern | ||
## Documentation | ||
## API Documentation | ||
- Installation | ||
- `.with()` | ||
- `.when()` | ||
- `.otherwise()` | ||
- Patterns | ||
- Literals | ||
- Objects and arrays | ||
- Sets and Maps | ||
- `__` and other wild cards | ||
- Objects | ||
- Arrays | ||
- Sets | ||
- Maps | ||
- `__` wildcard | ||
- `__.string` wildcard | ||
- `__.number` wildcard | ||
- `__.boolean` wildcard | ||
- `when` guards | ||
- `not` patterns | ||
- `select` pattern | ||
- Type inference | ||
## Pattern matching | ||
### Patterns | ||
#### Literals | ||
#### Object and arrays | ||
#### Sets and Maps | ||
#### Wildcards | ||
#### `when` guards | ||
#### `not` patterns | ||
#### `select` patterns | ||
### type inference | ||
@@ -105,0 +281,0 @@ |
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
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
29968
439
302