Skip to content

Instantly share code, notes, and snippets.

@sledorze
Last active July 17, 2023 10:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sledorze/bbfb8ace13560c94e84598b201f6e8b3 to your computer and use it in GitHub Desktop.
Save sledorze/bbfb8ace13560c94e84598b201f6e8b3 to your computer and use it in GitHub Desktop.
Type safe Typescript definition of an ad'hoc Fold for a Tagged Sum Type (multiple tags supported)
// Definition
type Compact<A> = { [k in keyof A]: A[k] }
type FStruct<R extends Record<any, any>, K extends keyof R = keyof R> = {
[k in K]: { [kv in R[k]]: R extends { [r in k]: kv } ? Compact<R> : never }
}
type Match<StructK, R> = { [KV in keyof StructK]: (v: StructK[KV]) => R }
const folder = <A extends object>() => <D extends keyof A>(discr: D) => <R>(match: Match<FStruct<A>[D], R>) => (a: A) =>
match[a[discr]](a as any)
const folderWiden = <A extends object>() => <D extends keyof A>(discr: D) => <
M extends Match<FStruct<A>[D], any>
>(
match: M
) => (a: A): M[keyof M] extends (a: any) => infer R ? R : never => match[a[discr]](a as any)
// Usage
type A = { type: 'ta'; x: 'a' } | { type: 'tb'; x: 'b'; y: 'c' } // Adhoc Union
const aByType = folder<A>()('type') // Define the discriminant
const matcher = aByType({ // a Matcher function
ta: v => v.x,
tb: v => v.y
})
const res = matcher({ type: 'ta', x: 'a' }) // Application
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment