Skip to content

Instantly share code, notes, and snippets.

@bakerface
Last active December 29, 2022 14:39
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 bakerface/90f42fdd3b53d10dee9d8ccd88805141 to your computer and use it in GitHub Desktop.
Save bakerface/90f42fdd3b53d10dee9d8ccd88805141 to your computer and use it in GitHub Desktop.
TypeScript pattern matching in 2 lines of code
import * as Maybe from "./maybe";
import { caseOf } from "./one-of";
const square = (n: number) => n * n;
// serializable as plain js arrays
console.log(Maybe.of(42)); // [ 'Just', 42 ]
// supports exhaustive pattern matching
caseOf(Maybe.map(Maybe.of(3), square), {
Nothing: () => console.log("Nothing"),
Just: (value) => console.log("Just " + value), // matches this pattern
});
// supports partial pattern matching
caseOf(Maybe.map(Maybe.of<number>(null), square), {
Just: (value) => console.log("Just " + value),
_: () => console.log("Nothing"), // matches this pattern
});
import { caseOf, OneOf, oneOf } from "./one-of";
export type Maybe<T> = OneOf<Variants<T>>;
export interface Variants<T> {
Nothing: [];
Just: [value: T];
}
export function of<T>(value: T | null | undefined): OneOf<Variants<T>> {
if (typeof value === "undefined" || value === null) {
return oneOf("Nothing");
}
return oneOf("Just", value);
}
export function map<T, R>(
maybe: OneOf<Variants<T>>,
fn: (value: T) => R
): OneOf<Variants<R>> {
return caseOf(maybe, {
Nothing: () => oneOf("Nothing"),
Just: (value) => oneOf("Just", fn(value)),
});
}
export function oneOf<T>(...choice: OneOf<T>): OneOf<T>;
export function oneOf<T>(): OneOf<T> {
return [].slice.call(arguments) as any; // eslint-disable-line
}
export function caseOf<T, Return>(
oneOf: OneOf<T>,
pattern: CaseOfPattern<T, Return>
): Return {
return (pattern[oneOf[0]] || (pattern as any)._)(...oneOf.slice(1));
}
export type OneOf<T> = {
[K in keyof T]: OneOfKey<K, T[K]>;
}[keyof T];
type OneOfKey<T, A> = A extends any[] ? [type: T, ...args: A] : never;
export type CaseOfPattern<T, Return> =
| ExhaustiveCaseOfPattern<T, Return>
| PartialCaseOfPattern<T, Return>;
type ExhaustiveCaseOfPattern<T, Return> = {
[K in keyof T]: CaseOf<T[K], Return>;
};
type PartialCaseOfPattern<T, Return> = {
_(): Return;
} & Partial<ExhaustiveCaseOfPattern<T, Return>>;
type CaseOf<A, Return> = A extends any[] ? (...args: A) => Return : never;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment