Home > Net >  Converting an enum to an array of custom literals
Converting an enum to an array of custom literals

Time:01-30

Assume this:

export enum Day {
  Monday = 111,
  Tuesday = 222,
  Wednesday = 333,
  Thursday = 444,
  Friday = 555,
  Saturday = 666,
  Sunday = 777,
}

Object.values(Day)
  .filter(v => typeof v === 'number') // or filter(Number)
  .map(numeric => console.log('numeric', numeric))
  // more code to come

I am trying to build an array of custom objects out of an enum, such as { id: 111, label: 'Monday' } and I am struggling with types.

When hovering over numeric I would expect the type to be number, or maybe Day, but never unioned with | string, as I am filtering strings out just before it.

Why is that?

CodePudding user response:

You can use type guard before map if you want typescript to infer the actual type for numeric, e.g.

function isNumber(v: unknown): v is number {
  return typeof v === 'number'
}
Object.values(Day)
  .filter(isNumber) // or filter(Number)
  .map(numeric => console.log('numeric', numeric)) // numeric is number

CodePudding user response:

The key is in how TypeScript converts enum code into JavaScript:

export var Day;
(function (Day) {
    Day[Day["Monday"] = 111] = "Monday";
    Day[Day["Tuesday"] = 222] = "Tuesday";
    Day[Day["Wednesday"] = 333] = "Wednesday";
    Day[Day["Thursday"] = 444] = "Thursday";
    Day[Day["Friday"] = 555] = "Friday";
    Day[Day["Saturday"] = 666] = "Saturday";
    Day[Day["Sunday"] = 777] = "Sunday";
})(Day || (Day = {}));

You can verify this for yourself by looking at the JS output in the TypeScript Playground

Basically, it makes sure that an enum's values can be looked up in either direction. What is Day.Monday? Why it's 111. But what is Day['111']? Ah, it's 'Monday'.

This works because the expressions in the inner square brackets resolve to the value being assigned. For example, Day["Monday"] = 111 resolves to 111, so Day[Day["Monday"] = 111] = "Monday"; is essentially shorthand for:

Day["Monday"] = 111;
Day[111] = "Monday";

So when you pass Day to Object.values you aren't just getting [111, 222, 333, 444, 555, 666, 777] like you might expect. Instead, you're getting ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", 111, 222, 333, 444, 555, 666, 777]. TypeScript interprets this as being of type (string | Day)[].

  •  Tags:  
  • Related