407

What is the proper way to loop through literals of an enum in TypeScript?

(I am currently using TypeScript 1.8.1.)

I've got the following enum:

export enum MotifIntervention {
    Intrusion,
    Identification,
    AbsenceTest,
    Autre
}

export class InterventionDetails implements OnInit
{
    constructor(private interService: InterventionService)
    {
        let i:number = 0;
        for (let motif in MotifIntervention) {
            console.log(motif);
        }
    }

The result displayed is a list

0
1
2
3
Intrusion,
Identification,
AbsenceTest,
Autre

I do want only four iterations in the loop as there are only four elements in the enum. I don't want to have 0 1 2 and 3 that seem to be index numbers of the enum.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Anthony Brenelière
  • 60,646
  • 14
  • 46
  • 58

1 Answers1

666

Two options:

for (let item in MotifIntervention) {
    if (isNaN(Number(item))) {
        console.log(item);
    }
}

Or

Object.keys(MotifIntervention).filter(key => !isNaN(Number(MotifIntervention[key])));

(code in playground)


Edit

String enums look different than regular ones, for example:

enum MyEnum {
    A = "a",
    B = "b",
    C = "c"
}

Compiles into:

var MyEnum;
(function (MyEnum) {
    MyEnum["A"] = "a";
    MyEnum["B"] = "b";
    MyEnum["C"] = "c";
})(MyEnum || (MyEnum = {}));

Which just gives you this object:

{
    A: "a",
    B: "b",
    C: "c"
}

You can get all the keys (["A", "B", "C"]) like this:

Object.keys(MyEnum);

And the values (["a", "b", "c"]):

Object.keys(MyEnum).map(key => MyEnum[key])

Or using Object.values():

Object.values(MyEnum)
nickf
  • 537,072
  • 198
  • 649
  • 721
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • 4
    It doesn't work because 0 1 2 3 are strings. – Anthony Brenelière Sep 07 '16 at 14:46
  • 5
    That's why there's a check for `isNaN(Number(...))`. It works in the playground I shared – Nitzan Tomer Sep 07 '16 at 15:01
  • Yes it works, I must have missed something. I noticed it's the best solution that is not in the duplicated question. – Anthony Brenelière Sep 08 '16 at 07:32
  • 11
    Thanks for this approach. For the `Object.keys` method, you'll want to use `Object.keys(MotifIntervention).filter(key => isNaN(Number(key)))`. – Jon Onstott Feb 13 '18 at 17:56
  • Shorter version that works with strict type checking: ```Object.keys(MotifIntervention).filter(key => isNaN(parseInt(key)))``` Doesn't work if some of the keys are integers, but I don't see why anyone would need it. – Joald Aug 07 '18 at 08:02
  • Since this answer didn't *quite* get me there, if it's helpful for anyone else using string enum to create an option list: `Object.keys(MyEnum).map(key => \`\`);` – knickum Aug 23 '18 at 20:23
  • 8
    This doesn’t work if you're expecting to loop through values of the enum type. So `for (let item in MotifIntervention) { doThing(item) }` will fail if `typeof doThing` function is `(arg: MotifIntervention) => void`. That's because the `for..in` loop will treat `item` as type `string`, not type `MotifIntervention`. – chharvey Sep 24 '18 at 18:00
  • @chharvey At runtime `MotifIntervention` is an object with two types of keys: numbers (but as strings) for the enum ordinals (i.e.: `"0"`, `"1"`, `"2"`...) and the strings for the enum value names (i.e.: `"Intrusion"`, `"Identification"`...). The `MotifIntervention` is `number` as far as the compiler is concerned, and so `isNaN(Number(item))` filters those out as the OP asked for the enum value names. If you want the ordinals (aka `MotifIntervention`) then just do `!isNaN(Number(item))`. – Nitzan Tomer Sep 24 '18 at 18:48
  • 9
    @NitzanTomer - that sounds like a good workaround at runtime, but i'm talking about compile time. When you attempt to call `doThing(item)` and `doThing` is of type `(arg: MotifIntervention) => void`, you'll get a compile time error **"Argument of type 'string' is not assignable to parameter of type 'MotifIntervention'."** – chharvey Sep 24 '18 at 19:08
  • @chharvey Oh, well, in that case, you'll need to pass `Number(item)` and not `item`, so something like: `for (...) { const ordinal = Number(item); if (!isNaN(ordinal)) { doThing(ordinal); } }` – Nitzan Tomer Sep 24 '18 at 19:12
  • You can also cast it, if you need to pass the enum value as an enum type.. `for (let motif in MotifIntervention) { doThing(motif as MotifIntervention) }` – Cheruvim Aug 12 '19 at 19:32
  • What @chharvey has brought up is an error I also encountered. This other question addresses it in a way that I was happy to do: https://stackoverflow.com/questions/36316326/typescript-ts7015-error-when-accessing-an-enum-using-a-string-type-parameter – Adam Apr 16 '20 at 18:22
  • 1
    I also include (typeof enumType[key] !== "function") in the filter to ignore methods I may have added to the enum type. – Etherman Jun 10 '20 at 15:45
  • Why exactly does JavaScript include enum values in `Object.keys`? – Audiopolis Jul 31 '22 at 21:20
  • 1
    this will not work for `enum X { "012" = 1 }` – ZiiMakc May 28 '23 at 15:56