npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-tagged-union

v1.2.1

Published

A modern TypeScript library designed to reduce boilerplate for tagged unions, also known as discriminated unions. This library is also an implementation of algebraic data types

Downloads

50

Readme

A modern TypeScript library designed to reduce boilerplate for tagged unions, also known as discriminated unions.
This library is also an implementation of algebraic data types.

Features

  • Effortlessly defines tagged union types, encompassing even recursive ones
  • Generates following helper functions for each tagged union type (without code generation 👍)
    1. Data constructors
    2. Pattern matching functions
    3. Type guard functions (type predicates)
  • Works on both browsers and Node.js
  • 0 dependencies

Basic example

Here is an example of defining a simple tagged union type and creating its values.

import { type TaggedUnion, createHelperFunctions } from 'ts-tagged-union'

// Define a tagged union type
export type Color = TaggedUnion<{
  rgb: { r: number; g: number; b: number }
  primary: {}
  secondary: {}
}>

// Get helper functions for the type
export const Color = createHelperFunctions<Color>()

// Create object with a data constructor
const rgb = Color.rgb({ r: 255, g: 31, b: 0 })
const primary = Color.primary() // {} can be omitted

console.log(rgb) // { r: 255, g: 31, b: 0, [Symbol(defaultTagKey)]: 'rgb' }
console.log(primary) // { [Symbol(defaultTagKey)]: 'primary' }

Pattern matching

To perform pattern matching with exhaustiveness checking, use the match function.

const color = Math.random() < 0.5 ? Color.primary() : Color.secondary()

const cssColor = Color.match(color, {
  rgb: ({ r, g, b }) => `rgb(${r}, ${g}, ${b})`,
  primary: () => '#C0FFEE', 
  secondary: () => 'blue',
})

The third argument serves as a so-called default case, as follows.

const isAchromatic = Color.match(
  color,
  { rgb: ({ r, g, b }) => r === g && g === b },
  (other) => false,
)

To perform pattern matching without exhaustiveness checking, use the matchPartial instead.

Type guard functions

Type guard functions are available as the is and isNot properties, as shown below.

if (Color.is.rgb(color)) {
  // Here, the variable is narrowed to the rgb variant type.
  console.log(color.r, color.g, color.b)
}

if (Color.isNot.secondary(color)) {
  // Here, the variable is narrowed to the rgb or primary variant type.
  console.log(color)
}

Custom tag key

The key of the property used to distinguish each variant is called tag key.
You can specify a tag key as the second argument to TaggedUnion<T> as follows.

// Define a tagged union type with a custom tag key, 'status'
type Response = TaggedUnion<
  {
    Success: { payload: Blob }
    Failure: { message: string }
  },
  'status' // Either a string literal or symbol type
>
// You need to provide the tag key as an argument due to TypeScript specifications.
const Response = createHelperFunctions<Response>('status')

const failure = Response.Failure({ message: 'Not found' })
console.log(failure.status) // Failure
console.log(Response.tagKey) // status

Adapters for tagged union types defined without using this library

createHelperFunctions and other utilities do not work for tagged union types without a tag-key-pointer.
The tag-key-pointer is a special hidden property that specifies which property is a tag.
It exists only at the type level, so it does not affect runtime.

The type defined with TaggedUnion<T> has the tag-key-pointer property.
To manually add it to an existing type, use AddTagKeyPointer as follows.

import { type AddTagKeyPointer, createHelperFunctions } from 'ts-tagged-union'

type RawTaggedUnion =
  | { type: 'circle', radius: number }
  | { type: 'rect', width: number; height: number }

type Shape = AddTagKeyPointer<RawTaggedUnion, 'type'>
const Shape = createHelperFunctions<Shape>('type')

If you need to remove the tag-key-pointer, use RemoveTagKeyPointer.

Other utilities

There are also several other utilities.

TagKeyOf<T>

Get the tag key of the given tagged union type.

type Response = TaggedUnion<
  {
    Success: { payload: Blob }
    Failure: { message: string }
  },
  'status'
>

type TagKey = TagKeyOf<Response> // 'status'

VariantOf<T, Tag>

Extract the variant type with the specific tag from a tagged union type.

type Response = TaggedUnion<
  {
    Success: { payload: Blob }
    Failure: { message: string }
  },
  'status'
>

type Variant = VariantOf<Response, 'Failure'> // { status: 'Failure', message: string }

PayloadOf<T, Tag>

Extract the payload type of the variant with the specific tag from a tagged union type.

type Response = TaggedUnion<
  {
    Success: { payload: Blob }
    Failure: { message: string }
  },
  'status'
>

type Payload = PayloadOf<Response, 'Failure'> // { message: string }