Peter Morlion logo

Blog

Iterating a TypeScript Enum

Here’s an overview of all the ways I’ve found to iterate an enum in TypeScript. I was particularly looking for how to iterate over the value of an enum, but still have the strongly typed value, instead of the underlying (string) value. But I decided to make this post about all possible ways I know of.

Default

Take this enum:

enum Color {
    Red, Green
}

Now add this code to log the values:

for (const value in Color) {
    log(value);
}

function log(value: string) {
    console.log(`Value: ${value}`);
}

Note: I’m using a separate log function to show what type the value is. You’ll see why later

When we run this code, we see:

Value: 0
Value: 1
Value: Red
Value: Green

So that’s a special thing to note! The enum actually contains both the numerical and the string value. You can see this in the resulting Javascript code in the TypeScript Playground.

Number Values

If we only want the numbers, we can use this code to log:

function log(value: string) {
    if (isNaN(Number(value))) {
        return;
    }

    console.log(`Value: ${value}`);
}

Member Names

What if we want only the member names? Easy, we can specify the underlying values when we define our enum:

enum Color {
    Red = "RED",
    Green = "GREEN"
}

Using the same loop and console code, we now get this:

Value: Red
Value: Green

You could also leave the enum as it was (keeping both the underlying number and string values), and check the type when logging:

function log(value: string) {
    if (!isNaN(Number(value))) {
        return;
    }
    
    console.log(`Value: ${value}`);
}

String Values

You can loop over an enum and get the member names as well:

enum Color {
    Red,
    Green
}

for (const value in Object.keys(Color)) {
    if (typeof Color[value] !== "string") {
        continue;
    }

    log(Color[Number(value)]);
}

function log(value: string) {
    console.log(`Value: ${value}`);
}

If the enum is backed by string values, this won’t work because the “value” in our loop is a string instead of a number. You might want to use this code:

enum Color {
    Red = "RED",
    Green = "GREEN"
}

function log(value: string) {
    console.log(`Value: ${value}`);
}

for (const value in Color) {
    log(Color[value]); //  <----- Error
}

But this won’t work because “value” is a string to TypeScript, but the indexer of “Color” can only accept “Red” or “Green”. So we need a helper method to create a new type:

function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
    return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[];
}

We can now use this code:

function log(value: Color) {
    console.log(`Value: ${value}`);
}

for (const value of enumKeys(Color)) {
    log(Color[value]);
}

This gives us the following result:

Value: RED
Value: GREEN

Also, notice how our “log” function now can accept a strongly-typed “Color” instead of a “string.”

Conclusion

So that’s basically it. There is code to get all the underlying values, the member names, the string values, and the strongly-typed values.

5 Responses

  1. Thanks a lot for that!

    I understand that they want to minimize the runtime impact of TypeScript but the lack of this function in TS has always been a pain point for me. I wrote that same function a few times, but not as concisely as you!


  2. const values = Object.values(Color);

    values.forEach((value) => {
    console.log("Value: " + value);
    });

    Prints:

    Value: RED
    Value: GREEN

    1. I guess that’s basically a variant of my first version (under “Default”)? But when I test it, it also prints the numerical values.

      1. Object.values(Enum).forEach(…) is equivalent to:

        for (const var of Object.values(Enum)) {…}

        the key difference here is that using ‘of’ instead of ‘in’ returns the enum values.

Leave a Reply

Your email address will not be published. Required fields are marked *