JSPM

  • Created
  • Published
  • Downloads 2815602
  • Score
    100M100P100Q194896F
  • License MIT

Typescript pattern matching library

Package Exports

  • ts-pattern

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (ts-pattern) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

TS Pattern

The Pattern Matching library for TypeScript you have been missing.

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 implemented in Elixir, Rust, Haskell, Swift and many other languages. There is a tc39 proposal 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.

Features

  • 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).

Gist

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.

type State =
  | { status: 'loading' }
  | { status: 'success'; data: string }
  | { status: 'error'; error: Error };

type Event =
  | { type: 'fetch' }
  | { type: 'success'; data: string }
  | { type: 'error'; error: Error };
import { match, __, not } 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]) => ({
      status: 'success',
      data: event.data,
    }))
    // 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: not('loading') }, { type: 'fetch' }], () => ({
      status: 'loading',
    }))
    // `__` is a wildcard, it will match any value.
    // You can use it at the top level, or inside a data structure.
    .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);

Code Sandbox Examples

Documentation

  • Installation
  • Patterns
    • Literals
    • Objects and arrays
    • Sets and Maps
    • __ and other wild cards
    • when guards
    • not patterns
    • select pattern

Pattern matching

type inference

type Input = { type: string } | string;

match<Input, 'ok'>({ type: 'hello' })
  .with(__, (value) => 'ok') // value: Input
  .with(__.string, (value) => 'ok') // value: string
  .with(
    when((value) => true),
    (value) => 'ok' // value: Input
  )
  .with(not('hello'), (value) => 'ok') // value: Input
  .with(not(__.string), (value) => 'ok') // value: { type: string }
  .with(not(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 'hello') }, (value) => 'ok') // value: { type: string }
  .with({ type: not(__.string) }, (value) => 'ok') // value: { type: string }
  .with({ type: not(when(() => true)) }, (value) => 'ok') // value: { type: string }
  .with(not({ type: when(() => true) }), (value) => 'ok') // value: string
  .with(not({ type: __.string }), (value) => 'ok') // value: string
  .run();