Reputation: 787
Given by these 2 string enums with always the same keys:
enum StatusEnum {
SUCCESS = 'SUCCESS',
ERROR = 'ERROR',
WARN = 'WARN',
DEBUG = 'DEBUG',
}
enum RawStatusEnum {
SUCCESS = 'Success',
ERROR = 'Error',
WARN = 'Warn',
DEBUG = 'Debug',
}
I want to cast value of enum RawStatusEnum
to value of enum StatusEnum
, that is:
const rawEnumValue = RawStatusEnum.ERROR // RawStatusEnum
const normalEnumValue = cast(rawEnumValue) // StatusEnum
But I have problems with typings and ... I've learnt about reverse mapping in typescript here, but reverse mapping works only for numeric enums. Thanks in advance.
Upvotes: 3
Views: 1663
Reputation: 328433
As you noted, string enums
don't automatically get a reverse mapping, so getting the key from a value isn't as simple as a lookup.
However, it's fairly straightforward to write a helper function to generate a reverse mapping for a string enum. But the compiler can't verify that it's implemented properly so you'll need a type assertion. For example:
const reverseStringEnum = <T extends Record<keyof T, string>>(e: T) =>
Object.fromEntries(Object.entries(e).map(([k, v]) => [v, k])) as
{ [K in keyof T as T[K]]: K };
It uses the Object.entries()
and Object.fromEntries()
methods to split the enum object into key-value pairs and then put it back together with the keys and values swapped. The input object type is T
, and the output object type is {[K in keyof T as T[K]]: K}
, which uses key remapping to swap the key and value types also.
Let's test it out on RawStatusEnum
:
const RawStatusReverseEnum = reverseStringEnum(RawStatusEnum);
/* const RawStatusReverseEnum: {
readonly Success: "SUCCESS";
readonly Error: "ERROR";
readonly Warn: "WARN";
readonly Debug: "DEBUG";
} */
Looks good. And now we can "cast" a RawStatusEnum
to a StatusEnum
by doing a pair of lookups. We lookup the key in RawStatusReverseEnum
and then use the key to lookup the value in StatusEnum
:
const castRawToNormal = <T extends RawStatusEnum>(t: T) =>
StatusEnum[RawStatusReverseEnum[t]];
Let's use it:
const rawEnumValue = RawStatusEnum.ERROR // RawStatusEnum
const normalEnumValue = castRawToNormal(rawEnumValue);
// const normalEnumValue: StatusEnum.ERROR
console.log(normalEnumValue); // "ERROR"
Looks good. The compiler even knows that normalEnumValue
will be of type StatusEnum.ERROR
.
Upvotes: 5