Enums are ubiquitous in modern programming. They are normally widely used to model categories of things, such as the possible states of a traffic light, the days of the week, and the months of the year.
One of the advantages of enums is that they enable us to map short lists of values into numbers, making it easier to compare and work with them in general. Enums in TypeScript have evolved from simple mappings of names to numbers to more advanced structures that can include methods and parameters.
In this article, we’ll explore different approaches for iterating over enums in TypeScript.
Editor’s note: This article was last reviewed and updated in June 2025. The update includes clear, practical code examples for iterating over numeric and string enums using Object.keys()
, Object.values()
, and Object.entries()
. It also clarifies which enum types can be iterated at runtime, adds a comparison of string vs. numeric enums, introduces a reusable getEnumKeys()
utility function, and links to relevant community resources for further reading.
If you need to extract numeric keys or numeric values from an object in JavaScript, here are three common approaches:
Use Object.keys()
combined with filtering to get keys that represent numbers.
Use Object.values()
and filter for values that are numbers.
Use Object.entries()
to filter both keys and values based on whether they are numeric.
Here’s a quick code snippet illustrating each method:
// Option 1: Get numeric keys only (keys as strings, filter those that convert to numbers) const numericKeys = Object.keys(obj).filter(key => !isNaN(Number(key))); // Option 2: Get numeric values only const numericValues = Object.values(obj).filter(value => typeof value === 'number'); // Option 3: Get entries with numeric keys or numeric values (custom filter example) const numericEntries = Object.entries(obj).filter( ([key, value]) => !isNaN(Number(key)) || typeof value === 'number' );
TypeScript enums are simple objects. For example:
enum TrafficLight { Green = 1, Yellow, Red }
In the definition above, Green
is mapped to the number 1
. The subsequent members are mapped to auto-incremented integers. Hence, Yellow
is mapped to 2
, and Red
to 3
.
If we didn’t specify the mapping Green = 1
, TypeScript would pick 0
as a starting index.
Sometimes, we want to iterate over a TypeScript enum — for instance, to perform certain actions for every element in the enum, such as rendering options in a UI or validating input values.
However, enums in TypeScript can be either numeric or string-based, and this distinction impacts how we can iterate over them effectively.
Before diving into iteration techniques, it’s important to understand which methods work best depending on the enum type you’re working with.
Enum Type | Description | Suitable Iteration Methods |
---|---|---|
Numeric Enums | Map keys to numeric values, often with reverse mapping from values back to keys | Object.keys() with filtering, Object.entries() with filtering |
String Enums | Map keys to string values, no reverse mapping | Object.values() filtering by type, Object.entries() filtering |
Numeric enums create a reverse mapping that includes both keys and numeric values, which requires filtering to avoid duplicate entries. String enums are simpler objects without reverse mappings, so filtering by type is usually enough.
In the following sections, we’ll explore four common iteration techniques, and clarify which work best for numeric vs. string enums.
You can iterate over enums in TypeScript using one of the following approaches, depending on the enum type and your needs:
The simplest way to iterate over an enum in TypeScript is to convert it to an array using the inbuilt Object.keys()
and Object.values()
methods. The former returns an array containing the keys of the enum object, and the latter returns an array of the enum’s values.
The following code snippet shows how to use the inbuilt object method to list the keys and values of an enum:
const keys = Object.keys(TrafficLight) keys.forEach((key, index) => { console.log(`${key} has index ${index}`) })
The example above prints the following:
"1 has index 0" "2 has index 1" "3 has index 2" "Green has index 3" "Yellow has index 4" "Red has index 5"
See how the numeric keys appear first? This happens because numeric enums generate a reverse mapping — TypeScript compiles the enum to an object that contains both the forward mapping (key to value) and reverse mapping (value to key). The numeric keys “1”, “2”, “3” correspond to this reverse mapping.
If we want to only list the string keys, we’ll have to filter out the numeric ones:
const stringKeys = Object .keys(TrafficLight) .filter((v) => isNaN(Number(v))) stringKeys.forEach((key, index) => { console.log(`${key} has index ${index}`) })
In this case, the snippet above prints the following:
"Green has index 0" "Yellow has index 1" "Red has index 2"
From the output above, we can see that the index
parameter has nothing to do with the actual numeric value in the enum. In fact, it is just the index of the key in the array returned by Object.keys()
.
Similarly, we can iterate over the enum values:
const values = Object.values(TrafficLight) values.forEach((value) => { console.log(value) })
Again, the snippet above prints both string and numeric values:
"Green" "Yellow" "Red" 1 2 3
Let’s say we’re interested in the numeric values, not in the string ones. We can filter the latter out similar to before, using .filter((v) => !isNaN(Number(v)))
.
It’s worth noting that we have to filter the values only because we’re dealing with numeric enums. If we had assigned a string value to the members of our enumeration, we wouldn’t have to filter out numeric keys and values:
enum TrafficLight { Green = "G", Yellow = "Y", Red = "R" } Object.keys(TrafficLight).forEach((key, index) => { console.log(`${key} has index ${index}`) }) Object.values(TrafficLight).forEach((value) => { console.log(value) })
The snippet above prints what follows, where the first three lines are from the first forEach
loop and the last three lines are from the second forEach
loop:
"Green has index 0" "Yellow has index 1" "Red has index 2" "G" "Y" "R"
String enums are very useful, as they are more human-readable than numeric ones. We can also mix numeric and string enums, although it is not advisable to do so.
Using the Object.keys()
and Object.values()
methods to iterate over the members of enums is a simple solution. Nonetheless, it is not very type-safe, as TypeScript returns keys and values as strings or numbers, thus not preserving the enum typing.
Before we explore other options for iterating over enums, let’s talk about reverse mapping in TypeScript’s numeric enums.
Reverse mapping is a TypeScript feature that compiles numeric enums to objects with both a name → value
property assignment and a value → name
property assignment.
See the following numeric enum:
enum Drinks { WATER = 1, SODA, JUICE }
This enum would be compiled in TypeScript to an object with a form similar to the following:
const drinks = { '1': 'WATER', '2': 'SODA', '3': 'JUICE', 'WATER': '1', 'SODA': '2', 'JUICE': '3' }
This is useful for providing human-readable references to enum values and making debugging easier.
It’s also important to note that TypeScript only provides this functionality to numeric enums.
for
loopsInstead of relying on Object.keys()
and Object.values()
, another approach is to use for
loops to iterate over the keys and then use reverse mapping to get the enum values.
TypeScript offers three different kinds of for loop statements, including the for..in
and for..of
loops, which can be used to iterate an enum.
The for..in
statement loops through the keys of the enum; this works on both numeric and string enums. As mentioned earlier, numerical enums return both the defined keys and the assigned numerical value from reverse mapping so iterating over the defined keys alone will require some additional logic. With string enums, it will only loop through the defined keys. You can think of using for..in
as a loop of what Object.keys
returns.
On the other hand, the for..of
statement cannot be run directly on an enum like we run for..in
— it requires some additional logic to iterate an enum.
Let’s look at a few examples.
For..in
loop through numeric enumsenum TrafficLight { Green, Yellow, Red } for (const tl in TrafficLight) { const value = TrafficLight[tl] if (typeof value === "string") { console.log(`Value: ${TrafficLight[tl]}`) } }
The script above will print the following:
"Value: Green" "Value: Yellow" "Value: Red"
Notice that, in the example above, we filtered out the numeric values. This way, we can extract the member names of our enum. If we wanted to fetch them, instead of the string values, we could use a different guard in the if
statement: typeof value !==
"string"
.
For..in
loop through string enumsenum TrafficLight { Green = "G", Yellow = "Y", Red = "R" } for (const tl in TrafficLight) { console.log(`Value: ${TrafficLight[tl]}`) }
The script above will print the following:
Value: G Value: Y Value: R
With this example, we didn’t need the typeof value === "string"
condition check because string enums don’t have reverse mapping and won’t return any numerical keys. Our example logs our enum’s assigned values but if we wanted the keys, we could log the iterator tl
instead.
For..of
loop for enumsThe For..of
loop statement can be used to loop through a JavaScript iterable object. If we tried to use a for..of
loop directly on an enum as we did for..in
, we would get an error:
enum TrafficLight { Green = "G", Yellow = "Y", Red = "R" } for (const tl of TrafficLight) { console.log(`Value: ${tl}`) }
If we tried to run the above block, it would throw Type 'typeof TrafficLight' is not an array type or a string type.
This is because enums are not iterable.
To fix this, we have to provide the for..of
statement an iterable to loop through. An option would be to extract the keys of our enum using Object.keys()
for the for..of
loop to use:
enum TrafficLight { Green = "G", Yellow = "Y", Red = "R" } for (const tl of Object.keys(TrafficLight)) { console.log(`Value: ${TrafficLight[tl]}`) }
The script above will print:
Value: G Value: Y Value: R
Earlier, we talked about numeric enums being reverse-mapped. So while our above snippet works as expected for string enums, it will require a little tweaking for numeric enums as it did with the for..in
loops:
enum TrafficLight { Green = 1, Yellow, Red } for (const tl of Object.keys(TrafficLight)) { const value = TrafficLight[tl] if (typeof value === "string") { console.log(`Value: ${TrafficLight[tl]}`) } }
It will give us the following:
Value: Green Value: Yellow Value: Red
You could also choose to use the
for..of
loop on the values of the enum instead.
One of the benefits of defining an enum is that we provide an object with a set of limited constants.
We have explored several options to iterate an enum by converting it to an iterable object using Object.keys()
or Object.values
; however, with these options, we lose the strict typing of our enum to the specified keys alone because Object.keys()
returns an array of strings.
To fix this, we’ll have to explicitly inform TypeScript about the type of our enum’s keys:
enum TrafficLight { Green = "G", Yellow = "Y", Red = "R" } 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[] }
In the above code, the enumKeys
function simply extracts the keys of the enum and returns them as an array. With this, the return type of enumKeys(TrafficLight)
is ("Green" | "Yellow" | "Red")[]
.
The filter method
.filter(k => Number.isNaN(k))
is used to ensure support for numeric enums by extracting the numeric keys from reverse mapping.
This typed array can then be looped through using a for..of
loop:
for (const tl of enumKeys(TrafficLight)) { const value = TrafficLight[tl] if (typeof value === "string") { console.log(`Value: ${TrafficLight[tl]}`) } }
We use
for..of
rather thanfor…in
in our for loop because the latter returns an index of items in our typed array.
Lodash is a JavaScript library that provides many utility methods for common programming tasks. Such methods use the functional programming paradigm to let us write more concise and readable code.
To install Lodash into our project, we can run the following command:
npm install lodash --save
The npm install lodash
command will install the module, and the save
flag will update the contents of the package.json
file.
It turns out we can leverage its forIn
method to iterate over an enum in TypeScript:
import { forIn } from 'lodash' enum TrafficLight { Green = 1, Yellow, Red, } forIn(TrafficLight, (value, key) => console.log(key, value))
The forIn
method iterates over both keys and values of a given object, invoking, in its simplest form, a given function for each (key, value)
pair. It is essentially a combination of the Object.keys()
and Object.values()
methods.
As we might expect, if we run the example above, we’ll get both string and numeric keys:
1 Green 2 Yellow 3 Red Green 1 Yellow 2 Red 3
As before, we can easily filter out string or numeric keys, depending on our needs:
import { forIn } from 'lodash' enum TrafficLight { Green = 1, Yellow, Red, } forIn(TrafficLight, (value, key) => { if (isNaN(Number(key))) { console.log(key, value) } })
The example above prints the following:
Green 1 Yellow 2 Red 3
In this case, the type of key
is string
, whereas the type of value
is TrafficLight
. Hence, this solution preserves the typing of the value
:
import { forIn } from 'lodash' enum TrafficLight { Green = "G", Yellow = "Y", Red = "R" } forIn(TrafficLight, (value, key) => { if (isNaN(Number(key))) { console.log(key, value) } })
As we might expect, the example above prints the following:
Green G Yellow Y Red R
getEnumKeys()
for reusable enum key extractionTo simplify and encapsulate the process of retrieving the keys of a TypeScript enum, you can use the reusable utility function getEnumKeys()
. This helper works with both numeric and string enums and filters out any reverse-mapping numeric keys automatically. It returns a clean array of enum keys as strings, making enum key extraction straightforward and consistent across your codebase:
/** * Utility to get the string keys of a TypeScript enum. * Works with both numeric and string enums. * @param enumObj The enum object * @returns Array of enum keys as strings */ function getEnumKeys<T extends object>(enumObj: T): (keyof T)[] { return Object.keys(enumObj).filter(key => isNaN(Number(key))) as (keyof T)[]; } // Example usage: enum Colors { Red = 'RED', Green = 'GREEN', Blue = 'BLUE', } const keys = getEnumKeys(Colors); console.log(keys); // Output: ['Red', 'Green', 'Blue']
In this article, we explored multiple techniques to iterate through enums in TypeScript, including built-in object methods, for
loops, and third-party libraries like Lodash. By leveraging methods such as Object.keys()
and Object.values()
, we learned how to handle both string and numeric enums. Additionally, we saw how using for..in
and for..of
loops, combined with type filtering, can provide flexibility when iterating over enums.
As usual, there’s no unique “right” solution. The way you iterate over the key/values of your enums strongly depends on what you have to do and whether you wish to preserve the enum typing.
For more insights, check out these related articles from our blog:
Thanks for reading, and happy coding!
LogRocket lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks, and with plugins to log additional context from Redux, Vuex, and @ngrx/store.
With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.
Modernize how you understand your web and mobile apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowOpenAI vs open source LLMs for frontend developers: integration guide, costs, performance comparison, implementation tips.
Compare fine-tuning vs. RAG to design faster, smarter, and more responsive AI-powered frontend experiences.
Navigation menu errors are common, even for seasoned developers. Learn seven common navigation menu errors and how to solve them using CSS.
Compare the top React toast libraries for when it’s more trouble than it’s worth to create your own custom toast components.