3

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.

5
  • 1
    Do you need to use enums? Could you use const asserted objects instead? Commented Jun 2, 2022 at 20:38
  • not sure and do not know the actual difference, but if you think it can solve the issue -- I will vote your answer and perhaps use your solution. Of course, I prefer just enums and haven't heard yet about what you suggest Commented Jun 2, 2022 at 20:53
  • Does this approach meet your needs? It's a little weird to play with enums this way, so I don't know that I really recommend it, but the approach here is to programmatically generate the reverse enum for RawStatusEnum and then do a nested lookup. If it meets your needs I'll write up an answer explaining it; otherwise, what am I missing? Commented Jun 3, 2022 at 2:46
  • Hmm, I think yes, it does. Please, post your answer -- I'll accept it. And if you don't mind I'd like to learn about another better approach suggested by you (I mean using const enums or smth else), please Commented Jun 3, 2022 at 13:08
  • 1
    I think it would take me too far afield to try refactoring your code to something I'd recommend. I personally dislike enums for a number of reasons (I can go into these if necessary) but synthesizing a reverse map for string enums is straightforward enough that I think it's probably reasonable if you're otherwise happy with enums. Commented Jun 4, 2022 at 1:05

1 Answer 1

5

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.

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.