How to Use Enums in TypeScript

What you’ll build or solve

You’ll create enums using the two common patterns: numeric enums and string enums. By the end, you’ll know which one to pick and how to avoid common enum surprises.

When this approach works best

This method works best when:

  • You want named constants like Role.Admin instead of repeating strings.
  • You share the same set of values across many files and want one definition.
  • You need a runtime object, for example to iterate values or reference members.

Avoid enums when:

  • You only need a small set of strings for type checking. A union type is often simpler.

Prerequisites

  • TypeScript installed
  • Basic familiarity with variables and objects

Step-by-step instructions

Step 1: Create a numeric enum

Numeric enums assign numbers automatically, starting at 0.

enumStatus {
  Pending,// 0
  Approved,// 1
  Rejected// 2
}

You can also set a starting value:

enumHttpCode {
  Ok=200,
  NotFound=404
}

What to look for

  • Numeric enums generate a runtime mapping.
  • Reverse lookup works for numeric enums, like Status[0].

Example reverse mapping:

enumDirection {
  Up,
  Down
}

console.log(Direction[0]);// "Up"

Step 2: Create a string enum

String enums use explicit string values, which makes them easier to read in logs and APIs.

enumRole {
  Admin="admin",
  Editor="editor",
  Viewer="viewer"
}

String enums do not auto-increment. Every member needs a value.

What to look for

  • String enums are easier to store and send over an API.
  • There is no reverse mapping for string enums.

Examples you can copy

Example 1: Enum for function input

enumTheme {
  Light="light",
  Dark="dark"
}

functionsetTheme(theme:Theme) {
return`Theme set to${theme}`;
}

setTheme(Theme.Dark);

Example 2: Enum in a switch statement

enumDirection {
  Left="left",
  Right="right"
}

functionmove(direction:Direction) {
switch (direction) {
caseDirection.Left:
return"Moving left";
caseDirection.Right:
return"Moving right";
  }
}

Example 3: Enum in an object type

enumPriority {
  Low="low",
  Medium="medium",
  High="high"
}

typeTask= {
  title:string;
  priority:Priority;
};

consttask:Task= {
  title:"Pay bills",
  priority:Priority.High
};

Common mistakes and how to fix them

Mistake 1: Assigning a raw string instead of an enum member

What someone might do:

enumRole {
  Admin="admin",
  Editor="editor"
}

letrole:Role="admin";

Why it breaks:

The type is Role, so the value must be a member like Role.Admin.

Correct approach:

letrole:Role=Role.Admin;

Mistake 2: Using numeric enums when you need readable values

What someone might do:

enumStatus {
  Pending,
  Approved
}

Then save the value to a database expecting strings.

Why it breaks:

You end up storing 0 or 1, which is unclear outside your app.

Correct approach:

enumStatus {
  Pending="pending",
  Approved="approved"
}

Mistake 3: Using enums when a union type is simpler

What someone might do:

Create an enum for values that never need runtime behavior.

Why it adds friction:

Enums generate runtime code and require extra syntax.

Correct approach:

typeMode="on"|"off";

Use a union when you only need type checking.


Troubleshooting

If TypeScript says a value is not assignable, confirm you used the enum member, like Role.Admin, not a plain string.

If you see unexpected numbers in logs, you are using a numeric enum. Switch to a string enum if you need readable values.

If you rely on reverse mapping like Enum[0], that only works with numeric enums.

If your bundle grows and enums are used heavily, replace small enums with unions when runtime objects are not needed.


Quick recap

  • Numeric enums auto-number members and support reverse mapping.
  • String enums store readable values and work well for APIs and databases.
  • Use enums when you want shared named constants or runtime access.
  • Prefer union types when you only need type checking.