99

Say I have a TypeScript enum, MyEnum, as follows:

enum MyEnum {
    First,
    Second,
    Third
}

What would be the best way in TypeScript 0.9.5 to produce an array of the enum values? Example:

var choices: MyEnum[]; // or Array<MyEnum>
choices = MyEnum.GetValues(); // plans for this?
choices = EnumEx.GetValues(MyEnum); // or, how to roll my own?
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
lightw8
  • 3,262
  • 2
  • 25
  • 35

12 Answers12

198

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 to
var 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);
    }
}
David Sherret
  • 101,669
  • 28
  • 188
  • 178
  • Curiously (because many people have upvoted this answer), I can't get this to work in the TS Playground: https://shorturl.me/jJ8G2t Am i doing anything wrong? – Peter Apr 09 '20 at 07:16
  • 1
    @Peter I updated the answer to include information about string enums. Also, you'll want to use a `for of` statement instead of a `for in` – David Sherret Apr 09 '20 at 15:53
30

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 = Red
color key = ORANGE, value = Orange
color key = YELLOW, value = Yellow
color key = GREEN, value = Green
color key = BLUE, value = Blue
color key = INDIGO, value = Indigo
color key = VIOLET, value = Violet
*/

See online example on TypeScript playground

tanguy_k
  • 11,307
  • 6
  • 54
  • 58
  • This has the error `Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof Color'. No index signature with a parameter of type 'string' was found on type 'typeof Color'.` – Jonas Oct 25 '20 at 10:32
  • 1
    @Jonas I've fixed it with a cast: `Object.keys(Color) as (keyof typeof Color)[]` – tanguy_k Oct 25 '20 at 18:30
10

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.

wonea
  • 4,783
  • 17
  • 86
  • 139
x0n
  • 51,312
  • 7
  • 89
  • 111
9

You can add functions to get the names and indices of the enum:

enum MyEnum {
  First,
  Second,
  Third
}

namespace MyEnum {
  function isIndex(key):boolean {
    const n = ~~Number(key);
    return String(n) === key && n >= 0;
  }

  const _names:string[] = Object
      .keys(MyEnum)
      .filter(key => !isIndex(key));

  const _indices:number[] = Object
      .keys(MyEnum)
      .filter(key => isIndex(key))
      .map(index => Number(index));

  export function names():string[] {
    return _names;
  }

  export function indices():number[] {
    return _indices;
  }
}

console.log("MyEnum names:", MyEnum.names());
// Prints: MyEnum names: ["First", "Second", "Third"]

console.log("MyEnum indices:", MyEnum.indices());
// Prints: MyEnum indices: [0, 1, 2]

Note that you could just export the _names and _indices consts rather than exposing them through an exported function, but because the exported members are members of the enum it is arguably clearer to have them as functions so they are not confused with the actual enum members.

It would be nice if TypeScript generated something like this automatically for all enums.

wonea
  • 4,783
  • 17
  • 86
  • 139
Rich
  • 997
  • 9
  • 12
5

I used the solution proposed by David Sherret and wrote an npm library you can use named enum-values...

Git: enum-values

// Suppose we have an enum
enum SomeEnum {
  VALUE1,
  VALUE2,
  VALUE3
}

// names will be equal to: ['VALUE1', 'VALUE2', 'VALUE3']
var names = EnumValues.getNames(SomeEnum);

// values will be equal to: [0, 1, 2]
var values = EnumValues.getValues(SomeEnum);
Slava Shpitalny
  • 3,965
  • 2
  • 15
  • 22
3

A one-liner to get a list of entries (key-value objects/pairs):

Object.keys(MyEnum).filter(a=>a.match(/^\D/)).map(name=>({name, value: MyEnum[name] as number}));
Venryx
  • 15,624
  • 10
  • 70
  • 96
2
enum MyEnum {
    First, Second, Third, NUM_OF_ENUMS
}

for(int i = 0; i < MyEnum.NUM_OF_ENUMS; ++i) {
    // do whatever you need to do.
}
joe
  • 84
  • 2
  • 5
    This only works if your enum doesn't define any values (it is well packed and incremental). Enum values might be "First = 0x1000" or "PageNotFound = 404" for example. NUM_OF_ENUMS will always be one greater than the largest defined value, so 0x1001 or 405 in my examples. – Aku Apr 25 '16 at 06:15
2

If you want to associate strings values to your enum these methods don't works. To have a generic function you can do :

function listEnum(enumClass) {
    var values = [];
    for (var key in enumClass) {
        values.push(enum[key]);
    }
    values.length = values.length / 2;
    return values;
}

It's works because TypeScript will add keys in first step, and values in second step.

In TypeScript it's:

var listEnums = <T> (enumClass: any): T[]=> {
    var values: T[] = [];
    for (var key in enumClass) {
        values.push(enumClass[key]);
    }
    values.length = values.length / 2;
    return values;
};

var myEnum: TYPE[] = listEnums<TYPE>(TYPE);
wonea
  • 4,783
  • 17
  • 86
  • 139
Chklang
  • 857
  • 1
  • 8
  • 17
2

Iterating over an enum

String Enums are best used for this. Here is an example:

// This is a string enum
enum MyEnum {
    First = 'First',
    Second = 'Second',
    Third = 'Third',
}

// An enum is a TS concept
// However his MyEnum compiles to JS object:
//  {
//   "First": "First",
//   "Second": "Second",
//   "Third": "Third"
// } 


// Therefore we can get the keys in the following manner:
const keysArray = Object.keys(MyEnum);

for (const key of keysArray) {
    console.log(key)
}
// [LOG]: "First" 
// [LOG]: "Second" 
// [LOG]: "Third" 
Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
1

joe's answer just made me realize that is much more easier to rely on the first N numeric keys than making more complex testings:

function getEnumMembers(myEnum): string[]
{
    let members = []
    for(let i:number = 0; true; i++) {
        if(myEnum[i] === undefined) break
        members.push(myEnum[i])
    }

    return members
}

enum Colors {
    Red, Green, Blue
}

console.log(getEnumMembers(myEnum))
Community
  • 1
  • 1
cvsguimaraes
  • 12,910
  • 9
  • 49
  • 73
  • 4
    This is a dangerous assumption because it is possible to define the values assigned to enums and they don't need to be incremental and well packed. It's not uncommon to see bit masks in an enum for example, or perhaps a table of HTML error codes that start at 400. – Aku Apr 25 '16 at 06:08
  • https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order – Ruan Mendes Aug 30 '17 at 16:37
1

A type-safe solution could be as follows:

enum Color { 
  Blue = 'blue', 
  Green = 'green'
}

enum MoreColor {
    Yellow,
    Red
}

function getEnumValues<T, K extends keyof T>(enumType: T): Array<T[K]> {
  return getEnumKeys<T, K>(enumType).map((x) => enumType[x]);
}

function getEnumKeys<T, K extends keyof T>(enumType: T): Array<K> {
  return Object.keys(enumType)
    .filter((x) => Number.isNaN(Number(x)))
    .map((x) => x as K);
}

// return type is Color[]
const colorValues = getEnumValues(Color); // ["blue", "green"]

// return type is MoreColor[]
const moreColorValues = getEnumValues(MoreColor); // [0, 1]

// return type is Array<"Blue" | "Green">
const colorKeys = getEnumKeys(Color); // ["Blue", "Green"]

// return type is Array<"Yellow" | "Red">
const moreColorKeys = getEnumKeys(MoreColor); // ["Yellow", "Red"]

But keep in mind that this solution does not force you to pass just enums to the function.

Keyrad
  • 460
  • 4
  • 11
-1

for nodejs:

const { isNumber } = require('util');

Object.values(EnumObject)
      .filter(val => isNumber(val))
      .map(val => {
         // do your stuff
      })
Suben Saha
  • 1,778
  • 16
  • 21