How to programmatically enumerate an enum type? How to programmatically enumerate an enum type? typescript typescript

How to programmatically enumerate an enum type?


This is the JavaScript output of that enum:

var MyEnum;(function (MyEnum) {    MyEnum[MyEnum["First"] = 0] = "First";    MyEnum[MyEnum["Second"] = 1] = "Second";    MyEnum[MyEnum["Third"] = 2] = "Third";})(MyEnum || (MyEnum = {}));

Which is an object like this:

{    "0": "First",    "1": "Second",    "2": "Third",    "First": 0,    "Second": 1,    "Third": 2}

Enum Members with String Values

TypeScript 2.4 added the ability for enums to possibly have string enum member values. So it's possible to end up with an enum that look like the following:

enum MyEnum {    First = "First",    Second = 2,    Other = "Second"}// compiles tovar MyEnum;(function (MyEnum) {    MyEnum["First"] = "First";    MyEnum[MyEnum["Second"] = 2] = "Second";    MyEnum["Other"] = "Second";})(MyEnum || (MyEnum = {}));

Getting Member Names

We can look at the example immediately above to try to figure out how to get the enum members:

{    "2": "Second",    "First": "First",    "Second": 2,    "Other": "Second"}

Here's what I came up with:

const e = MyEnum as any;const names = Object.keys(e).filter(k =>     typeof e[k] === "number"    || e[k] === k    || e[e[k]]?.toString() !== k);

Member Values

Once, we have the names, we can loop over them to get the corresponding value by doing:

const values = names.map(k => MyEnum[k]);

Extension Class

I think the best way to do this is to create your own functions (ex. EnumEx.getNames(MyEnum)). You can't add a function to an enum.

class EnumEx {    private constructor() {    }    static getNamesAndValues(e: any) {        return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] as string | number }));    }    static getNames(e: any) {        return Object.keys(e).filter(k =>             typeof e[k] === "number"            || e[k] === k            || e[e[k]]?.toString() !== k        );    }    static getValues(e: any) {        return EnumEx.getNames(e).map(n => e[n] as string | number);    }}


With TypeScript >= 2.4 you can define string enums:

enum Color {  RED = 'Red',  ORANGE = 'Orange',  YELLOW = 'Yellow',  GREEN = 'Green',  BLUE = 'Blue',  INDIGO = 'Indigo',  VIOLET = 'Violet'}

JavaScript ES5 output:

var Color;(function (Color) {    Color["RED"] = "Red";    Color["ORANGE"] = "Orange";    Color["YELLOW"] = "Yellow";    Color["GREEN"] = "Green";    Color["BLUE"] = "Blue";    Color["INDIGO"] = "Indigo";    Color["VIOLET"] = "Violet";})(Color || (Color = {}));

Which is an object like this:

const Color = {  "RED": "Red",  "ORANGE": "Orange",  "YELLOW": "Yellow",  "GREEN": "Green",  "BLUE": "Blue",  "INDIGO": "Indigo",  "VIOLET": "Violet"}

Thus, in the case of string enums, no need to filter things,Object.keys(Color) and Object.values(Color) (*) are enough:

const colorKeys = Object.keys(Color) as (keyof typeof Color)[];console.log('colorKeys =', colorKeys);// ["RED", "ORANGE", "YELLOW", "GREEN", "BLUE", "INDIGO", "VIOLET"]const colorValues = Object.values(Color);console.log('colorValues =', colorValues);// ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"]colorKeys.map(colorKey => {  console.log(`color key = ${colorKey}, value = ${Color[colorKey]}`);});/*color key = RED, value = Redcolor key = ORANGE, value = Orangecolor key = YELLOW, value = Yellowcolor key = GREEN, value = Greencolor key = BLUE, value = Bluecolor key = INDIGO, value = Indigocolor key = VIOLET, value = Violet*/

See online example on TypeScript playground

(*) Polyfill needed for old browsers, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values#Browser_compatibility


There is no concept of RTTI (runtime type information) in TypeScript (think: reflection) so in order to do this, knowledge of the transpiled JavaScript is required. So, assuming TypeScript 0.95:

enum MyEnum {    First, Second, Third}

becomes:

var MyEnum;(function(MyEnum) {    MyEnum[MyEnum["First"] = 0] = "First";    MyEnum[MyEnum["Second"] = 1] = "Second";    MyEnum[MyEnum["Third"] = 2] = "Third";}

So, this is modeled as a regular object in javascript, where MyEnum.0 == "First" and MyEnum.First == 0. So, to enumerate all of the enum names, you need to get all properties that belong to the object and that are also not numbers:

for (var prop in MyEnum) {             if (MyEnum.hasOwnProperty(prop) &&        (isNaN(parseInt(prop)))) {        console.log("name: " + prop);    }}

Ok, so now I've told you how to do it, I'm allowed to tell you this is a bad idea. You're not writing a managed language, so you can't bring these habits. It's still just plain old JavaScript. If I wanted to use a structure in JavaScript to populate some kind of choices list, I would use a plain old array. An enum is not the right choice here, pun intended. The goal of TypeScript is to generate idiomatic, pretty JavaScript. Using enums in this way does not preserve this goal.