Skip to content

Instantly share code, notes, and snippets.

@isthatcentered
Last active October 24, 2022 12:34
Show Gist options
  • Save isthatcentered/fcc64aad800eb12e44061e985cfef41d to your computer and use it in GitHub Desktop.
Save isthatcentered/fcc64aad800eb12e44061e985cfef41d to your computer and use it in GitHub Desktop.
Typescript match discriminated union tag
type MatchTag = {
// Strict match, one handler must be provided for each possible tag
<
X extends { _tag: string },
K extends {
[k in X['_tag']]: (member: Extract<X, { _tag: k }>) => any
},
>(
handlers: K,
): (member: X) => ReturnType<K[keyof K]>
// Partial match, only provide a handler for the desired tag and handle the rest via a fallback handler
<
X extends { _tag: string },
K extends Partial<{
[k in X['_tag']]: (_: Extract<X, { _tag: k }>) => any
}>,
H,
>(
partialHandlers: K & {
[k in X['_tag']]?: (member: Extract<X, { _tag: k }>) => any
},
fallback: (_: Exclude<X, { _tag: keyof K }>) => H,
): (member: X) => { [k in keyof K]: ReturnType<NonNullable<K[k]>> }[keyof K] | H
}
/**
* Execute a function for each possible tag of a discriminated union
*
* Basically a more concise & expressive version of doing switch(union._tag) {case "blah": ...}
*/
export const matchTag: MatchTag = ((handlers: any, fallback: any) => (member: any) => {
if (!handlers[member['_tag']]) return fallback(member) as any
return handlers[member['_tag']](member) as any
}) as any
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment