11

I declare a map so I can convert the numeric enum value into the value our API expects.

export const StatusMap: ReadonlyMap<Status, string> = new Map([
    [Status.NEW, 'new'],
    [Status.PENDING, 'pending'],
]);

But when I do statusMap.get(Status.NEW) it always tells me that the return value is possibly undefined. Is there a way to force a map (or similar) to contain all enum values?

And yes I know you can technically do

export enum Status {
    NEW = 'new',
    PENDING = 'pending',
}

but let's be honest, this kind of ruins the point of enums (IMO).

14
  • If you can use regular object instead of Map it will be as simple as Record<Status, string>. But what's the point of mapping strings to numbers and vice versa if the api expects the strings? String enum doesn't ruin the point of enums Commented May 29, 2020 at 9:11
  • @AlekseyL. well, strings are way larger than numbers. It's not "enumerated" anymore. I could just do type Status = 'new' | 'pending'; Commented May 29, 2020 at 9:18
  • 1
    @AlekseyL. Problem is that I'm implementing a cache + a local store lol. So they're not just in the API but also caches separately. But I will definitely consider your argument. :) thanks Commented May 29, 2020 at 9:50
  • 1
    Anyway answering original question - you can define type StatusMap = { get<T extends Status>(status: T): string } and use it for statusMap. Pay attention, it won't verify that all enum values are actually in map typescriptlang.org/play/index.html#code/… Commented May 29, 2020 at 11:45
  • 1
    And here's version with object as map (it will verify all the enum members are listed in map) typescriptlang.org/play/index.html#code/… Commented May 29, 2020 at 11:48

1 Answer 1

17

If you can use regular object instead of Map, it can be defined as Record of enum members:

const statusMap: Record<Status, string> = {
    [Status.NEW]: 'new',
    [Status.PENDING]: 'pending',
};

Playground


Another option would be using type assertion that guarantees that all enum members are in the map:

type StatusMap = { get<T extends Status>(status: T): string }

const statusMap = new Map([
    [Status.NEW, 'new'],
    [Status.PENDING, 'pending'],
]) as StatusMap;

** Pay attention, with this approach it is not guaranteed that all the enum members are in the map at runtime, so if you choose to go this way - better to cover it by unit test.

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

3 Comments

In your first example, Record, is it appropriate/good form to get a value with statusMap[<one of Status>]? The reason I gravitated to a Map is to use .get().
@JoeSadoski could you elaborate on what's the issue with statusMap[Status.NEW]?
Sorry, please disregard that. I misunderstood how Record worked. I thought they worked like objects, where you could use dot notation OR bracket notation. Your TS Playground example explained their use better for me. Thank you!

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.