Skip to content

Instantly share code, notes, and snippets.

@tstodter
Last active October 1, 2019 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tstodter/2ea1671170b6927c0ed3b6c483e328dd to your computer and use it in GitHub Desktop.
Save tstodter/2ea1671170b6927c0ed3b6c483e328dd to your computer and use it in GitHub Desktop.
Pattern matching for sum types/algebraic data types in Typescript
export type UnionType = {kind: string};
export type UnionMemberByKind<U, K> = Extract<U, { kind: K }>
export type UnionMatchObj<U extends UnionType, Ret> = {
[K in U['kind']]: (unionMember: UnionMemberByKind<U, K>) => Ret
};
export type Merge<M extends {}, N extends {}> = {
[P in Exclude<keyof M, keyof N>]: M[P]
} & N;
export const match = <U extends UnionType, RetT>(
fObj: UnionMatchObj<U, RetT>
) => (
unionVal: U
) => (
fObj[unionVal.kind as U['kind']](unionVal as any)
);
export const matcher =
<U extends UnionType>() =>
<RetT>(fObj: UnionMatchObj<U, RetT>) => (
match<U, RetT>(fObj)
);
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
export const makeFactory = <T extends UnionType>(kind: T['kind']) =>
(init: PartialBy<T, 'kind'>): T => ({
...init,
kind
} as T);
/* ///////////////////////////////////// */
// For example...
/* ///////////////////////////////////// */
type Move = {
kind: 'move';
direction: string;
player: string;
};
type Attack = {
kind: 'attack';
target: string;
player: string;
};
type LeaveDungeon = {
kind: 'leaveDungeon';
player: string;
};
type DungeonEvent = Move | Attack | LeaveDungeon;
const Move = makeFactory<Move>('move');
const Attack = makeFactory<Attack>('attack');
const LeaveDungeon = makeFactory<LeaveDungeon>('leaveDungeon');
const toString = match<DungeonEvent, string>({
move: ({direction, player}) => `${player} moving ${direction}`,
attack: ({target, player}) => `${player} is attacking ${target}`,
leaveDungeon: ({player}) => `${player} has left the dungeon`
});
const newEvent = Move({direction: 'east', player: 'Zardul'});
console.log(toString(newEvent));
const secondEvent = LeaveDungeon({player: 'Zardul'});
console.log(toString(secondEvent));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment