Socket
Socket
Sign inDemoInstall

ts-pattern

Package Overview
Dependencies
Maintainers
1
Versions
151
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ts-pattern - npm Package Compare versions

Comparing version 0.3.1 to 0.3.2

6

lib/types/ExtractPreciseValue.d.ts

@@ -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",

# 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 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc