Created
November 23, 2023 01:21
-
-
Save oofdere/0bba1284ec50f01bebdb8edc7b768887 to your computer and use it in GitHub Desktop.
the discovery process for crabrave's enums
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
// 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); | |
} | |
console.log(packed); | |
for (const e of packed) { | |
console.log( | |
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