Skip to content

Instantly share code, notes, and snippets.

@bakerface
Created August 22, 2020 13:37
Show Gist options
  • Save bakerface/829d85d85cfcbecc5b41f5d7e671d3ed to your computer and use it in GitHub Desktop.
Save bakerface/829d85d85cfcbecc5b41f5d7e671d3ed to your computer and use it in GitHub Desktop.
Pattern matching for types at runtime in TypeScript
import { CaseOf, TypePattern, caseOf } from "./caseOf";
function success<T>(value: T): T {
return value;
}
const errorPattern: TypePattern<never> = {
Array() {
throw new TypeError("Array");
},
BigInt() {
throw new TypeError("BigInt");
},
Boolean() {
throw new TypeError("Boolean");
},
Null() {
throw new TypeError("Null");
},
Number() {
throw new TypeError("Number");
},
Object() {
throw new TypeError("Object");
},
String() {
throw new TypeError("String");
},
Symbol() {
throw new TypeError("Symbol");
},
Function() {
throw new TypeError("Function");
},
Undefined() {
throw new TypeError("Undefined");
},
};
function matches<T extends keyof TypePattern<void>>(type: T): CaseOf<void> {
return caseOf<void>({ ...errorPattern, [type]: success });
}
describe("caseOf", () => {
it("Array", () => matches("Array")([1, 2, 3]));
it("BigInt", () => matches("BigInt")(BigInt(0)));
it("Boolean", () => matches("Boolean")(true));
it("Null", () => matches("Null")(null));
it("Number", () => matches("Number")(0));
it("Object", () => matches("Object")({}));
it("String", () => matches("String")(""));
it("Symbol", () => matches("Symbol")(Symbol()));
it("Function", () => matches("Function")(() => null));
it("Undefined", () => matches("Undefined")(void 0));
});
export interface TypePattern<Return> {
Array(value: unknown[]): Return;
BigInt(value: bigint): Return;
Boolean(value: boolean): Return;
Function(value: Function): Return;
Null(value: null): Return;
Number(value: number): Return;
Object(value: Record<string, unknown>): Return;
String(value: string): Return;
Symbol(value: symbol): Return;
Undefined(value: undefined): Return;
}
export type CaseOf<Return> = (value: unknown) => Return;
export const caseOf = <Return>(
pattern: TypePattern<Return>
): CaseOf<Return> => (value): Return => {
switch (typeof value) {
case "bigint":
return pattern.BigInt(value);
case "boolean":
return pattern.Boolean(value);
case "function":
return pattern.Function(value);
case "number":
return pattern.Number(value);
case "object":
return value === null
? pattern.Null(value)
: Array.isArray(value)
? pattern.Array(value)
: pattern.Object(value as Record<string, unknown>);
case "string":
return pattern.String(value);
case "symbol":
return pattern.Symbol(value);
default:
return pattern.Undefined(void 0);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment