I am currently developing an API wrapper for a lower level library that utilizes enums to map human readable keys to internal values. In order to enhance security, I want to only use the enum keys and not the underlying values in any logging or other functions. I am seeking a way to dynamically create an object that mirrors the original enum but with keys/values that are identical. Additionally, I want to ensure that the parent object keys are passed as parameters similar to an enum, rather than allowing direct use of the string value.
enum Colors {
red = '$r5',
green = '$g2',
lightBlue = '$b9',
darkBlue = '$b1',
}
function getColor(color: Colors) {
return color;
}
getColor(Colors.darkBlue); // succeeds
getColor('darkBlue'); // fails as expected
function createKeyEnum<E>(e: E): {[k in keyof E]: k extends keyof E ? k : never} {
return Object.keys(e).reduce((p, v) => {
(p as any)[v] = v;
return p;
}, {} as {[k in keyof E]: k extends keyof E ? k : never});
}
const NewColors = createKeyEnum(Colors);
type NewColors = keyof typeof NewColors;
// forward mapping
function getOldColor(color: NewColors) {
return Colors[color];
}
// reverse mapping
function getNewColor(color: Colors) {
const reverse: any = {};
Object.entries(Colors).forEach(([k, v]) => reverse[v] = k);
return reverse[color] as NewColors;
}
getOldColor(NewColors.darkBlue); // succeeds
getOldColor('darkBlue'); // succeeds, but should fail like an enum
const color = getNewColor(Colors.darkBlue);
// typeof color == 'red' | 'green' | 'lightBlue' | 'darkBlue'
// should be: typeof color == NewColors
Is it possible to achieve this functionality with advanced typings, or are enums a unique feature that cannot be replicated using other TypeScript typings? In the code snippet provided, my NewColors types only result in a union of literal key strings. While this serves its purpose, it does permit users to utilize the string value directly. How can I associate it with the parent symbol NewColors and ensure that values are strictly derived from it like in an enum?