dismatchdismatch

Introduction

Type-safe discriminated unions for TypeScript. Define once — get constructors, type guards, exhaustive matching, async dispatch, and runtime validation.

dismatch is a small (~1.1 kB gzipped, zero dependencies) library that turns discriminated unions into a complete toolkit: constructors, type guards, exhaustive pattern matching, partial transforms, fold-based aggregation, async dispatch, and runtime validation — all generated from a single schema.

import { createUnion, is, type InferUnion } from 'dismatch';

const Shape = createUnion({
  circle: (radius: number) => ({ radius }),
  rectangle: (width: number, height: number) => ({ width, height }),
});

type Shape = InferUnion<typeof Shape>;

const area = Shape.match({
  circle: ({ radius }) => Math.PI * radius ** 2,
  rectangle: ({ width, height }) => width * height,
});

area(Shape.circle(5)); // 78.54
Try in Playground

switch gives you exhaustiveness. dismatch gives you exhaustiveness plus reusable typed matchers, map, fold, partition, count, is, runtime validation, and async — all from a single schema.

What sets it apart

  • No runtime wrappers. createUnion produces plain { type, ... } objects, so values cross network boundaries, debug cleanly, and interop with switch / ts-pattern without unwrapping. See Removing dismatch for the exit path.
  • Reusable handlers. match, map, fold return typed functions you can pass to array.map, pipe, or store in a variable. Other libraries make every match one-shot.
  • Variant-aware collections. count, partition, fold operate on whole arrays in a single pass, with both branches narrowed.
  • First-class async. matchAsync, matchAllAsync, foldAsync and friends return Promise<R> — never Promise<A> | Promise<B> — and freely mix sync handlers.
  • Runtime safety. isKnown validates incoming data against the declared schema; UnknownVariantError carries .variant and .known for clean reporting.
  • Companion RemoteData. A ready-made Idle | Loading | Refreshing | Ok | Failed union for async UI state, usable with every dismatch API.

Where to go next

On this page