1

I have a string enum defined as follows:

export enum UserRole {
  SHOP_DESIGNER = 'DG_PF00-CorpID-Designa_Shop-Designers',
  SHOP_EMPLOYEE = 'DG_PF00-CorpID-Designa_Shop-Employees',
  LOGISTICS_TEAM = 'DG_PF00-CorpID-Designa_Logistics',
}

I want to create a type-safe mapping from a an enum string value to its enum type:

const USER_ROLE_NAMES<string, UserRole> = ....

So I can parse an arbitrary value by querying the map:

let role: UserRole

// role = SHOP_DESIGNER 
role = USER_ROLE_NAMES.get('DG_PF00-CorpID-Designa_Shop-Designers')

// role = SHOP_DESIGNER 
role = USER_ROLE_NAMES.get('DG_PF00-CorpID-Designa_Shop-Employees')

// role = SHOP_DESIGNER 
role = USER_ROLE_NAMES.get('DG_PF00-CorpID-Designa_Logistics')

// role = undefined
role = USER_ROLE_NAMES.get('some arbitrary string value which is not an enum value')

I already tried the following:

export const USER_ROLE_NAMES = new Map(Object.entries(UserRole).map(([key, value]: [string, UserRole]) => [value, key]));

but then the type of USER_ROLE_NAMES is Map<UserRole, string> and the map cannot be queried with a string.

If I invert the mapping

export const USER_ROLE_NAMES = new Map(Object.entries(UserRole).map(([key, value]: [string, UserRole]) => [key, value]));

then the type is correct, but the mapping is wrong ('DESIGNA_USER' => 'DG_PF00-CorpID-Designa_Users',... instead of 'DG_PF00-CorpID-Designa_Users' => 'DESIGNA_USER',...

3
  • Hmm, the point of enums is that the values are meant to be opaque; UserRole is strictly narrower than the union of the string literal values. It is considered a type error to use the string literal "DG_PF00-CorpID-Designa_Shop-Designers" where UserRole.SHOP_DESIGNER is expected. Trying to get the compiler to do otherwise would be "type unsafe" according to the language. So when you say you want a type safe reverse mapping it sort of sounds like you want the opposite? Maybe we're just using different terms. Commented Apr 8, 2022 at 13:09
  • 1
    Anyway when people want to peer into enum values it's often a sign that enums are not what they really want. If you replace the enum with a const object then the particular problem you're talking about goes away, see this. Of course there are other problems: Map<K, V>.get(k) returns V | undefined regardless of what k is; the data structure has no notion of "definitely present" keys; and you can't get() an arbitrary string key either unless you make K a string. What do you want to do about those two issues? Are they relevant to the question? Commented Apr 8, 2022 at 13:24
  • Wait a minute I just re-read the question and it looks like you don't want a reverse mapping at all; you just want to downcast strings to their equivalent enum values. So you want stackoverflow.com/questions/43804805/… or stackoverflow.com/questions/59376564/… etc maybe? Commented Apr 8, 2022 at 13:36

1 Answer 1

3

At runtime, the value of UserRole.SHOP_DESIGNER is actually 'DG_PF00-CorpID-Designa_Shop-Designers', not 'SHOP_DESIGNER'. So the mapping you are asking for would look like this (at runtime):

const theMapping ={
  'DG_PF00-CorpID-Designa_Shop-Designers': 'DG_PF00-CorpID-Designa_Shop-Designers',
  ...
}

If you want this "enum value" -> "enum member" mapping, you can construct it like this: TypeScript playground link

export enum UserRole {
  SHOP_DESIGNER = 'DG_PF00-CorpID-Designa_Shop-Designers',
  SHOP_EMPLOYEE = 'DG_PF00-CorpID-Designa_Shop-Employees',
  LOGISTICS_TEAM = 'DG_PF00-CorpID-Designa_Logistics',
}

const mapping: Map<string, UserRole> = new Map(Object.values(UserRole).map(
    (memberValue) => [`${memberValue}`, memberValue] as const
))

let role1 = mapping.get('DG_PF00-CorpID-Designa_Shop-Designers')
let role2 = mapping.get('unknown')
Sign up to request clarification or add additional context in comments.

1 Comment

that's exactly what I was looking for

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.