discriminated
Helpers for tagged unions sharing a literal discriminant key.
import type {
NarrowByTag, OmitTag, TagsOf, PayloadOf, IsDiscriminated, Matchers,
} from '@gentleduck/ttest/discriminated'
Works on union types where every variant shares a kind / type / tag slot holding a literal.
NarrowByTag
type NarrowByTag<U, K extends keyof U, V>
Narrow U to the variant where U[K] === V.
type Shape =
| { kind: 'circle'; r: number }
| { kind: 'square'; side: number }
type Circle = NarrowByTag<Shape, 'kind', 'circle'> // { kind: 'circle'; r: number }
OmitTag
type OmitTag<U, K extends keyof U>
Strip the tag property K from every variant of U.
type ShapePayload = OmitTag<Shape, 'kind'>
// { r: number } | { side: number }
TagsOf
type TagsOf<U, K extends keyof U>
Union of all tag values in slot K.
type Tags = TagsOf<Shape, 'kind'> // 'circle' | 'square'
PayloadOf
type PayloadOf<U, K extends keyof U, V>
Tag-stripped variant whose tag at K equals V.
type CirclePayload = PayloadOf<Shape, 'kind', 'circle'> // { r: number }
IsDiscriminated
type IsDiscriminated<U, K extends keyof U>
true if U has 2+ variants with distinct tag values at K.
Matchers
type Matchers<U, K extends keyof U, R> = {
[V in U extends unknown ? U[K] & PropertyKey : never]: (variant: NarrowByTag<U, K, V>) => R
}
Pattern-match handler object — one method per tag value.
type ShapeMatchers = Matchers<Shape, 'kind', number>
// {
// circle: (v: { kind: 'circle'; r: number }) => number
// square: (v: { kind: 'square'; side: number }) => number
// }