Last active
December 29, 2022 14:39
-
-
Save bakerface/90f42fdd3b53d10dee9d8ccd88805141 to your computer and use it in GitHub Desktop.
TypeScript pattern matching in 2 lines of code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)), | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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