Skip to content

Instantly share code, notes, and snippets.

Created November 23, 2023 01:21
Show Gist options
  • Save oofdere/0bba1284ec50f01bebdb8edc7b768887 to your computer and use it in GitHub Desktop.
Save oofdere/0bba1284ec50f01bebdb8edc7b768887 to your computer and use it in GitHub Desktop.
the discovery process for crabrave's enums
// use "Twoslash Query Comments" extension in VSCode to see type comments
// enums are simply defined as interfaces
interface Types {
Num: number;
Str: string;
Bool: boolean;
Obj: { field: "value" };
Undef: undefined;
// we can grab the keys and values as follows:
type KTypes = keyof Types; //=>
type VTypes = Types[keyof Types]; //=>
// we can use this monstrosity that maps each entry to a new object, and then turns the values into a union
type KVUnion = {
[K in keyof Types]: [K, Types[K]]; //=>
}[keyof Types]; //=>
// then if we use that as a function type, we're pretty much done:
type EnumFn<R> = (...entry: KVUnion) => R;
function test(...entry: KVUnion) {
return entry[1];
test("Bool", true); //=>
test("Num", 0); //=>
test("Obj", { field: "value" });
test("Undef", undefined);
// note that the whole union is returned, this is a problem in some cases, but for us it's workable
// converting all that to generics gives us:
type KTypes<T> = keyof T; //=>
type VTypes<T> = T[keyof T]; //=>
type KVUnion<T> = {
[K in keyof T]: [K, T[K]]; //=>
}[keyof T]; //=>
// we can make a type to hold the key and value:
type PackedEnumValue<K, V> = {
k: K;
v: V;
type AnyPackedEnumValue<T> = PackedEnumValue<KTypes<T>, VTypes<T>>;
// we make a function to pack our entries into an object:
function pack<Enum>(...entry: KVUnion<Enum>): AnyPackedEnumValue<Enum> {
return { k: entry[0], v: entry[1] };
const packed = [
pack<Types>("Str", "string"),
pack<Types>("Num", 1),
pack<Types>("Obj", { field: "value" }),
pack<Types>("Bool", true),
// finally, we make a match function:
type Functionify<T, U> = {
[K in keyof T]: (val: T[K]) => U;
function match<Enum, Ret>(
pattern: AnyPackedEnumValue<Enum>,
arms: Functionify<Enum, Ret>
) {
return arms[pattern.k](pattern.v);
for (const e of packed) {
match<Types, string>(e, {
Str: (e) => `this string is "${e}"`,
Num: (e) => `this number is ${e}`,
Obj: (e) => `this is truly an ${e} moment`,
Bool: (e) => `this bool is ${e}`,
Undef: (e) => "nothing to see here",
// function which takes in an enum interface E and keys into the interface to accept one of the <K, V>s in E, returns a PackedEnumValue<E, K, V>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment