Question
In TypeScript, how can I iterate over an enum and get the name of each enum member?
For example:
enum MyEnum {
Entry1,
Entry2
}
for (const entry in MyEnum) {
// How can I use the enum member name here,
// such as "Entry1" or "Entry2"?
}
I want to loop through the enum object and access each declared symbol name.
Short Answer
By the end of this page, you will understand how TypeScript enums are represented at runtime, why iterating over them can produce unexpected results, and how to safely get enum member names. You will also learn the difference between numeric and string enums, common iteration patterns, and when to use alternatives like Object.keys() or Object.values().
Concept
TypeScript enums are a way to define a fixed set of named constants.
For example:
enum MyEnum {
Entry1,
Entry2
}
This creates named members:
MyEnum.Entry1MyEnum.Entry2
But an important detail is that enums also exist as JavaScript objects at runtime. That means you can loop over them with normal object tools like:
for...inObject.keys()Object.values()Object.entries()
Why iteration can be confusing
Numeric enums in TypeScript create reverse mappings.
This enum:
enum MyEnum {
Entry1,
Entry2
}
is roughly compiled into something like:
{
0: "Entry1",
1: "Entry2",
Entry1: 0,
Entry2: 1
}
So if you iterate over it directly, you do not only get the member names. You also get the numeric keys.
That is why beginners often see both of these during iteration:
"0""1""Entry1""Entry2"
Why this matters
In real programs, you may want to:
- build dropdown options from an enum
- validate input against allowed values
- display human-readable labels
- generate configuration lists
If you do not understand enum runtime behavior, your loops may include unwanted keys or values.
Mental Model
Think of a numeric enum like a bilingual dictionary.
It lets you look up in both directions:
- name -> number
- number -> name
So for:
enum MyEnum {
Entry1,
Entry2
}
TypeScript stores both:
Entry1 -> 00 -> Entry1Entry2 -> 11 -> Entry2
When you loop through the enum object, you are opening the whole dictionary, not just the list of original names.
If you only want the declared member names, you need to filter out the numeric side.
Syntax and Examples
Basic enum
enum MyEnum {
Entry1,
Entry2
}
Using for...in
for (const key in MyEnum) {
console.log(key);
}
For a numeric enum, this prints:
0
1
Entry1
Entry2
That happens because numeric enums include reverse mapping.
Get only the enum member names
A common approach is to filter out numeric keys:
enum MyEnum {
Entry1,
Entry2
}
for (const key in MyEnum) {
if (isNaN(Number(key))) {
console.log(key);
}
}
Output:
Entry1
Entry2
Why this works
"0"becomes number0, so it is numeric"1"becomes number1, so it is numeric"Entry1"becomesNaN, so it is not numeric"Entry2"becomesNaN, so it is not numeric
Using Object.keys()
enum MyEnum {
Entry1,
Entry2
}
const names = Object.keys(MyEnum).filter(key => isNaN(Number(key)));
console.log(names);
Output:
["Entry1", "Entry2"]
This is often cleaner than a for...in loop.
String enums are simpler
enum Status {
Pending = "PENDING",
Done = "DONE"
}
console.log(Object.keys(Status));
Output:
["Pending", "Done"]
String enums do not create reverse mappings, so iteration is usually more predictable.
Reusable helper
function getEnumKeys<T extends object>(enumObj: T): string[] {
return Object.keys(enumObj).filter(key => isNaN(Number(key)));
}
enum MyEnum {
Entry1,
Entry2
}
console.log(getEnumKeys(MyEnum));
Output:
["Entry1", "Entry2"]
Step by Step Execution
Consider this code:
enum MyEnum {
Entry1,
Entry2
}
const keys = Object.keys(MyEnum);
const names = keys.filter(key => isNaN(Number(key)));
console.log(keys);
console.log(names);
Step 1: Define the enum
enum MyEnum {
Entry1,
Entry2
}
At runtime, this behaves roughly like:
{
0: "Entry1",
1: "Entry2",
Entry1: 0,
Entry2: 1
}
Step 2: Read all object keys
const keys = Object.keys(MyEnum);
keys becomes something like:
["0", "1", "Entry1", "Entry2"]
Step 3: Filter out numeric keys
const names = keys.filter(key => isNaN(Number(key)));
Each item is checked:
"0"->Number("0")is0-> numeric -> remove it"1"->Number("1")is1-> numeric -> remove it"Entry1"->Number("Entry1")isNaN-> keep it"Entry2"->Number("Entry2")isNaN-> keep it
Step 4: Final result
console.log(names);
Output:
["Entry1", "Entry2"]
This gives you the enum member names only.
Real World Use Cases
Building dropdown menus
You may have an enum for status values:
enum Status {
Draft,
Published,
Archived
}
You can extract names to generate select options.
Validating incoming values
If an API or form sends a value that must match an enum, you can compare against enum keys or values.
Rendering labels in admin panels
Enums are often used for roles, states, categories, or modes. Iterating the enum lets you render all available choices.
Configuration and feature flags
A project may define a small set of supported environments or modes using an enum, then loop through them when building config tools.
Logging and debugging
When debugging state transitions, enum names are easier to read than raw numbers.
Real Codebase Usage
In real codebases, developers often avoid raw for...in loops on enums unless they specifically understand the enum shape.
Common patterns include:
Using Object.keys() with filtering
const enumNames = Object.keys(MyEnum).filter(key => isNaN(Number(key)));
This is common for numeric enums.
Using string enums for cleaner runtime behavior
enum Role {
Admin = "ADMIN",
User = "USER"
}
String enums avoid reverse mapping, which makes iteration easier.
Creating helper utilities
Projects often define one helper and reuse it:
function enumKeys(enumObj: object): string[] {
return Object.keys(enumObj).filter(key => isNaN(Number(key)));
}
Validation patterns
const allowedStatuses = Object.values(Status);
This is useful when checking user input or API payloads.
Mapping to UI data
const options = Object.keys(Status).map(key => ({
label: key,
value: Status[key as keyof typeof Status]
}));
This pattern is common in forms and component libraries.
Guarding against enum surprises
Teams often document whether an enum is numeric or string-based so iteration behaves as expected.
Common Mistakes
Mistake 1: Assuming for...in gives only enum names
Broken expectation:
enum MyEnum {
Entry1,
Entry2
}
for (const key in MyEnum) {
console.log(key);
}
Many beginners expect:
Entry1
Entry2
But they actually get numeric keys too.
Fix
for (const key in MyEnum) {
if (isNaN(Number(key))) {
console.log(key);
}
}
Mistake 2: Forgetting the difference between numeric and string enums
Numeric enum:
enum A {
One,
Two
}
String enum:
enum B {
One = "ONE",
Two = "TWO"
}
String enums do not have reverse mapping, so iteration is simpler.
Mistake 3: Using enum names when you actually need enum values
enum Status {
Pending,
Done
}
const names = Object.keys(Status).filter(key => isNaN(Number(key)));
console.log(names);
This gives:
["Pending", "Done"]
But the values are:
[0, 1]
Know whether you need:
- member names
- member values
- both
Mistake 4: Treating enums like arrays
Broken idea:
console.log(MyEnum.length);
Enums are objects, not arrays. Use Object.keys() or Object.values() instead.
Mistake 5: Not being explicit in UI code
If you build UI options from a numeric enum without filtering, you may show duplicate or invalid entries.
Better
const options = Object.keys(MyEnum)
.filter(key => isNaN(Number(key)))
.map(key => ({ label: key, value: MyEnum[key as keyof typeof MyEnum] }));
Comparisons
| Approach | Works with numeric enums | Works with string enums | Returns | Notes |
|---|---|---|---|---|
for...in | Yes | Yes | Keys | Numeric enums include reverse-mapping keys |
Object.keys() | Yes | Yes | Keys | Usually the clearest choice |
Object.values() | Yes | Yes | Values | Numeric enums may include both names and numbers |
Object.entries() | Yes | Yes | Key-value pairs | Useful when building label/value lists |
Numeric enum vs string enum
| Enum type | Example | Reverse mapping | Easier to iterate? |
|---|---|---|---|
| Numeric enum | enum A { One, Two } | Yes | No |
| String enum | enum B { One = "ONE", Two = "TWO" } | No | Yes |
for...in vs Object.keys()
| Option | Best use | Why |
|---|---|---|
for...in | Manual looping | Fine, but you usually need filtering |
Object.keys() | Getting enum names as an array | Cleaner and easier to combine with filter() and map() |
In practice, Object.keys() is often the easiest way to get enum member names.
Cheat Sheet
Get enum names from a numeric enum
enum MyEnum {
Entry1,
Entry2
}
const names = Object.keys(MyEnum).filter(key => isNaN(Number(key)));
Get enum names with for...in
for (const key in MyEnum) {
if (isNaN(Number(key))) {
console.log(key);
}
}
Numeric enums
enum A {
One,
Two
}
Runtime shape includes both:
A.One === 0A[0] === "One"
String enums
enum B {
One = "ONE",
Two = "TWO"
}
- no reverse mapping
- easier to iterate
Quick rules
- Enums are runtime objects.
- Numeric enums create reverse mappings.
Object.keys()is usually the clearest way to get names.- Filter numeric keys when using numeric enums.
- Use string enums if you want simpler iteration behavior.
Useful helper
function getEnumNames(enumObj: object): string[] {
return Object.keys(enumObj).filter(key => isNaN(Number(key)));
}
FAQ
Why does iterating a TypeScript enum return numbers and names?
Because numeric enums create reverse mappings at runtime. The enum object stores both name -> value and value -> name.
How do I get only enum member names in TypeScript?
Use:
Object.keys(MyEnum).filter(key => isNaN(Number(key)))
Do string enums have reverse mapping?
No. String enums do not create reverse mappings, so their keys are simpler to iterate.
Should I use for...in or Object.keys() for enums?
Object.keys() is usually clearer because it returns an array you can easily filter and map.
Can I use Object.values() on enums?
Yes, but with numeric enums it can include both names and numeric values, so be careful.
Are TypeScript enums arrays?
No. Enums are objects at runtime.
What is the easiest enum type to iterate?
String enums are usually easier because they do not include reverse mapping.
Mini Project
Description
Create a small TypeScript utility that turns an enum into a list of options for a dropdown menu. This demonstrates how to safely extract enum member names and pair them with values, which is a common task in forms and admin dashboards.
Goal
Build a function that converts a TypeScript enum into an array of { label, value } objects using only the real enum member names.
Requirements
- Define one numeric enum and one string enum.
- Write a reusable function to extract only enum member names.
- Create a second function that converts an enum into dropdown-style option objects.
- Print the results for both enums.
- Make sure numeric enum reverse-mapping keys are excluded.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.