Question

How to Loop Through Enum Values in TypeScript

typescriptjavascriptarraysenumselement

Question

In TypeScript, what is the correct way to loop through the members of an enum?

I am using this enum:

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

And in my class I am doing this:

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

The output is:

0
1
2
3
Intrusion
Identification
AbsenceTest
Autre

I want the loop to run only once for each enum member, because the enum has only four elements. I do not want the 0, 1, 2, and 3 entries, which appear to be numeric indexes from the enum.

Short Answer

By the end of this page, you will understand why looping over a numeric enum in TypeScript shows both numbers and names, how enum reverse mapping works, and how to get only the values you want for tasks like rendering radio buttons.

Concept

TypeScript enums are compiled into JavaScript objects. For numeric enums, TypeScript creates a two-way mapping:

  • from name to number
  • from number back to name

For example, this enum:

enum MotifIntervention {
  Intrusion,
  Identification,
  AbsenceTest,
  Autre
}

becomes conceptually similar to:

{
  0: "Intrusion",
  1: "Identification",
  2: "AbsenceTest",
  3: "Autre",
  Intrusion: 0,
  Identification: 1,
  AbsenceTest: 2,
  Autre: 3
}

That is why a for...in loop prints both numeric keys and string keys.

This matters because enums are often used for:

  • form options
  • dropdowns
  • radio buttons
  • status values
  • API-related constants

If you do not understand the reverse mapping behavior, you may accidentally render duplicate options or process the wrong values.

The main idea is:

  • numeric enums include reverse mappings
  • string enums do not
  • when looping, you usually need to filter the keys or values

For display in UI elements such as radio buttons, developers often extract only the string names or only the actual enum values, depending on what the UI needs.

Mental Model

Think of a numeric enum like a bilingual dictionary:

  • one side says Intrusion -> 0
  • the other side says 0 -> Intrusion

When you loop through the whole object, you are opening both sides of the dictionary, so you see entries in both directions.

If you only want labels for radio buttons, you should read just one side of the dictionary instead of both.

Take Quiz

Syntax and Examples

The most common way to loop through only the names of a numeric enum is to filter out numeric keys.

enum MotifIntervention {
  Intrusion,
  Identification,
  AbsenceTest,
  Autre
}

const names = Object.keys(MotifIntervention).filter(key => isNaN(Number(key)));

console.log(names);
// ["Intrusion", "Identification", "AbsenceTest", "Autre"]

Why this works

  • Object.keys(...) returns all keys from the enum object
  • numeric enums contain keys like "0", "1", as well as "Intrusion"
  • filter(key => isNaN(Number(key))) keeps only the non-numeric keys

Example for radio buttons

enum MotifIntervention {
  Intrusion,
  Identification,
  AbsenceTest,
  Autre
}

const motifOptions = Object.keys(MotifIntervention)
  .filter(key => isNaN(Number(key)));

for (const option of motifOptions) {
  console.log(option);
}

Output:

Intrusion
Identification
AbsenceTest
Autre

If you want numeric values instead

const values = Object.values(MotifIntervention)
  .filter(value => typeof value === "number");

console.log(values);
// [0, 1, 2, 3]

String enum alternative

If you define the enum as a string enum, looping becomes simpler because there is no reverse mapping:

enum MotifIntervention {
  Intrusion = "Intrusion",
  Identification = "Identification",
  AbsenceTest = "AbsenceTest",
  Autre = "Autre"
}

console.log(Object.keys(MotifIntervention));
// ["Intrusion", "Identification", "AbsenceTest", "Autre"]

This is often easier for display purposes.

Step by Step Execution

Consider this code:

enum MotifIntervention {
  Intrusion,
  Identification,
  AbsenceTest,
  Autre
}

const allKeys = Object.keys(MotifIntervention);
console.log(allKeys);

const namesOnly = allKeys.filter(key => isNaN(Number(key)));
console.log(namesOnly);

Step 1: The enum is created

TypeScript builds an object that contains:

{
  0: "Intrusion",
  1: "Identification",
  2: "AbsenceTest",
  3: "Autre",
  Intrusion: 0,
  Identification: 1,
  AbsenceTest: 2,
  Autre: 3
}

Step 2: Object.keys reads all keys

allKeys becomes something like:

["0", "1", "2", "3", "Intrusion", "Identification", "AbsenceTest", "Autre"]

Step 3: Filter removes numeric keys

For each key:

  • "0" → Number("0") is 0 → numeric → remove it
  • "1" → numeric → remove it
  • "Intrusion" → Number("Intrusion") is NaN → keep it
  • "Identification" → NaN → keep it

Step 4: Final result

namesOnly becomes:

["Intrusion", "Identification", "AbsenceTest", "Autre"]

That is the list you would typically use to render radio buttons or a select menu.

Real World Use Cases

Enums are often looped over when you need to build UI or enforce a controlled set of values.

Common use cases

  • Radio buttons for predefined choices such as status or category
  • Dropdown menus for user-selectable options
  • Validation rules where input must match one of several allowed values
  • API payloads where only known values are accepted
  • Configuration settings such as themes, roles, or modes

Example: rendering radio button options

const motifOptions = Object.keys(MotifIntervention)
  .filter(key => isNaN(Number(key)));

You can then bind motifOptions in your component template.

Example: validating input

function isValidMotif(value: string): boolean {
  return Object.keys(MotifIntervention)
    .filter(key => isNaN(Number(key)))
    .includes(value);
}

This helps ensure only valid enum names are accepted.

Take Quiz

Real Codebase Usage

In real projects, developers usually do not loop over enums directly inside random parts of the code every time. Instead, they create a reusable list of options.

Common pattern: build UI options once

enum MotifIntervention {
  Intrusion,
  Identification,
  AbsenceTest,
  Autre
}

const motifOptions = Object.keys(MotifIntervention)
  .filter(key => isNaN(Number(key)))
  .map(key => ({
    label: key,
    value: MotifIntervention[key as keyof typeof MotifIntervention]
  }));

This produces objects like:

[
  { label: "Intrusion", value: 0 },
  { label: "Identification", value: 1 },
  { label: "AbsenceTest", value: 2 },
  { label: "Autre", value: 3 }
]

Why this pattern is useful

  • keeps templates simpler
  • avoids repeating filtering logic
  • works well for forms and components
  • makes testing easier

Guard clause idea

If enum values come from user input or APIs, developers often validate them before using them:

function toMotif(value: number): MotifIntervention | null {
  if (MotifIntervention[value] === undefined) {
    return null;
  }

  return value as MotifIntervention;
}

Real project tip

If the enum is mainly for display text, a string enum or a plain array of option objects is often easier to maintain than a numeric enum.

Common Mistakes

1. Using for...in without understanding enum reverse mapping

Broken example:

for (const key in MotifIntervention) {
  console.log(key);
}

This prints both numeric and string keys for numeric enums.

Fix: filter the keys.

for (const key of Object.keys(MotifIntervention).filter(k => isNaN(Number(k)))) {
  console.log(key);
}

2. Assuming enums behave like arrays

Broken example:

console.log(MotifIntervention.length);

Enums are objects, not arrays. They do not have an array length.

Fix: use Object.keys(...) and filter if needed.

3. Mixing up enum names and enum values

console.log(MotifIntervention.Intrusion); // 0
console.log(MotifIntervention[0]); // "Intrusion"

Be clear whether you need:

  • the display name: "Intrusion"
  • the stored numeric value: 0

4. Using numeric enums when string enums would be clearer

If your goal is UI display, numeric enums may add extra work.

Better option:

enum MotifIntervention {
  Intrusion = "Intrusion",
  Identification = "Identification",
  AbsenceTest = "AbsenceTest",
  Autre = "Autre"
}

5. Forgetting that older TypeScript code may need simpler patterns

Some modern examples use newer helpers or syntax. If you are working in older TypeScript codebases, Object.keys(...).filter(...) is often the safest readable approach.

Comparisons

ApproachWhat it gives youGood forNotes
for...in on numeric enumNumeric and string keysRarely useful directlyCauses duplicate-looking output
Object.keys(enum).filter(...)Enum member names onlyDisplay labels, radio buttonsCommon solution for numeric enums
Object.values(enum).filter(...)Values onlyGetting numeric enum valuesSimpler in newer JS environments
String enum + Object.keys(...)Clean names onlyUI-friendly enumsNo reverse mapping
Plain array of objectsFull control over labels and valuesForms and component UIsOften easiest in real apps

Numeric enum vs string enum

Enum typeReverse mappingEasy to loop for UIExample value
Numeric enumYesNeeds filtering0
String enumNoYes"Intrusion"

Enum vs plain array

OptionBest whenExample
EnumYou need named constants in codestatuses, modes, roles
Plain arrayYou mainly need display optionsradio buttons, select lists
Take Quiz

Cheat Sheet

Numeric enum

enum MotifIntervention {
  Intrusion,
  Identification,
  AbsenceTest,
  Autre
}

Get only enum names

const names = Object.keys(MotifIntervention)
  .filter(key => isNaN(Number(key)));

Get only numeric values

const values = Object.values(MotifIntervention)
  .filter(value => typeof value === "number");

Build radio button options

const options = Object.keys(MotifIntervention)
  .filter(key => isNaN(Number(key)))
  .map(key => ({
    label: key,
    value: MotifIntervention[key as keyof typeof MotifIntervention]
  }));

String enum version

enum MotifIntervention {
  Intrusion = "Intrusion",
  Identification = "Identification",
  AbsenceTest = "AbsenceTest",
  Autre = "Autre"
}

Key rule

  • Numeric enums have reverse mapping.
  • String enums do not.
  • If looping numeric enums, filter out numeric keys.

FAQ

Why does TypeScript enum iteration show both names and numbers?

Because numeric enums are compiled into objects with reverse mappings. They store both name -> value and value -> name.

How do I loop through only enum names in TypeScript?

Use:

Object.keys(MyEnum).filter(key => isNaN(Number(key)))

How do I loop through only enum values?

For numeric enums, use:

Object.values(MyEnum).filter(value => typeof value === "number")

Are string enums easier to use for display?

Yes. String enums do not create reverse mappings, so they are often simpler for UI display.

Should I use enums for radio button labels?

You can, but in many real applications a plain array of { label, value } objects is even clearer, especially when labels need formatting.

Is for...in wrong for enums?

Not exactly, but for numeric enums it gives both directions of the mapping, which is usually not what you want for display.

Why are the numeric keys strings when looping?

Object keys in JavaScript are strings, so keys like 0 and 1 are returned as "0" and "1".

Take Quiz

Mini Project

Description

Create a small TypeScript utility that turns an enum into a list of radio button options. This demonstrates how to safely loop through enum members and prepare data for UI rendering without including unwanted numeric reverse-mapping keys.

Goal

Build a reusable function that converts a numeric enum into a clean array of { label, value } option objects.

Requirements

  • Create a numeric enum with at least four members.
  • Write a function that extracts only the enum names.
  • Convert those names into { label, value } objects.
  • Print the resulting options array.
  • Make sure no numeric keys appear in the final output.
Take Quiz

Keep learning

Related questions

View more