Last active
June 9, 2021 01:26
-
-
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
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
/* | |
* 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