ts-tagged-union v1.2.1
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 👍)
- Data constructors
- Pattern matching functions
- 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 }
8 months ago
10 months ago
11 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago