Skip to content

Instantly share code, notes, and snippets.

@tvler
Last active June 9, 2021 01:26
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 tvler/45a471f9ca1255734d8066551f84f604 to your computer and use it in GitHub Desktop.
Save tvler/45a471f9ca1255734d8066551f84f604 to your computer and use it in GitHub Desktop.
A function to create namespaced enums in typescript, where the return type is a literal reflection of the returned enum value itself
/*
* TS Playground: https://www.typescriptlang.org/play?noUncheckedIndexedAccess=true&noUnusedLocals=true&noUnusedParameters=true#code/PQKgUABCEEIKYHMCWA7CcAeBDAtgBwBs5IRgwwBjAexQGcAXdbfIiAXggoCc4t64Acrji08WCnAAmAURQBXHAAoA3pAi049AIIAuCAG0A5FkMAaCIYBGhgLqm1G+jD1GT5q2YsVb9gL4BKAG5yTFxCOAA6Ry0IrAhgYAgAImjYpJDmcKjNGAiKeMSUnLz0sFASCFlJJjCiEjIwADM5FAp6JBpOHj5BYVFxKVkFAB41IRwRMQkmfhRJWnV6LlQEewhK+RwAaTgATxm4OYWGZZRVtSHtvYXMWfmILS4uLF3hy53dgD41iABJFDwckYt0O9wASnBqFxJMNxpMBuZ9BFke9rjZPmBPoo1KhAfQ9P88fZ-HpVOt9Cg+lM4BBUJUMEtxPQPrRhoSgZ8bKS1OTDgoPrS0AAVOThaRECYoehC3Z4OBsgFAilUgborkQAAGABJlJSJv0JL4Ijq+Vddr4NcF1r5gr4IGSIPRZTTLgA1LAEOQ0jja5Rwg1wI061Hmy1qJ1yiAABSwXHaHv91Jkm3YEAhUJhiYRachVGhb02H3Mbo9Xs+n2CamodEYYjjSATKokyYUtD0j2erxj9cb+qTl0+qcUAHlLAArSH0CKHJZIESKXFA-wQLALDsvUbrckAaz2eizEnMADdS3A9CHaDY1J9-BEcFg8IpFMq+9nTSybMu2IOHetqwx0E2Ucx1TZQ7VXHMMwLfk9mLTZ3U9OAKx5CBGjzCBFH-YFCz2CAqEaQCYN2Whl1-LcsMInAEK9c94NPVNfT1eFDWNZR3z2C0VwWEtEKtLd1lNYD9HY3YbFTU1qLgPjrXIfieHoOQuDQZQDCYgN1UE8cIHAhZu3jAgD0GTY+ICSs-xoAC1P7FMOGAydYloWgkAQFAVF8cxkQiOt9MMlscBIvj5MUtArIGPzbUrCMXQZZ42hZYYhQOI57QMbc9BOFZ1SwFB9l8QcOESkFktQRo4C4CAAHE1AAfk1HUMrOCAADIIF3XZ8Mqi01D0FA4CPMrIudCARTFCUZxlOUEtFVgivuddXmyr58uG6a4ApBRLDKmxAiAA
*/
/*
* A function to create namespaced enums, where the return type
* is a literal reflection of the returned enum value itself.
*
* Ex:
* createNamespacedEnum({
* setA: ['a', 'b'],
* setB: ['a', 'b'],
* });
*
* // Generated object and type:
* {
* setA: { a: 'setA.a', b: 'setA.b' }
* setB: { a: 'setB.a', b: 'setB.b' }
* }
*/
const createNamespacedEnum = <
Namespace extends string,
EnumKey extends string,
EnumKeys extends Array<EnumKey>,
Input extends Record<Namespace, [...EnumKeys]>
>(
input: Input,
): {
[namespace in ExtractKeys<Input>]: {
[enumKey in TupleElementType<Input[namespace]>]: `${namespace}.${enumKey}`;
};
} => {
type EnumValue = `${Namespace}.${EnumKey}`;
type PartialNamespacedEnum = Record<Namespace, Record<EnumKey, EnumValue>>;
const partialNamespacedEnums: Array<PartialNamespacedEnum> = (Object.entries(input) as Array<
[key: Namespace, value: EnumKeys]
>).map(([namespace, enumKeys]) => {
const enumObj = {} as Record<EnumKey, EnumValue>;
for (const enumKey of enumKeys) {
const enumValue: EnumValue = `${namespace}.${enumKey}` as EnumValue;
enumObj[enumKey] = enumValue;
}
return { [namespace]: enumObj } as PartialNamespacedEnum;
});
const namespacedEnum = Object.assign({}, ...partialNamespacedEnums);
return namespacedEnum;
};
/*
* Extracts the keys of a type, and casts them all to be strings
* (a key can naturally be a string, number, or symbol).
*/
type ExtractKeys<T extends { [k: string]: any }> = T extends infer G
? `${string & keyof G}`
: never;
/*
* Gets all the element types of a tuple and returns them all as a union
*/
type TupleElementType<Tuple extends Array<any>> = Tuple[number];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment