Skip to content

TypeScript Enums — Complete Guide with Examples

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about TypeScript Enums. We cover key concepts, practical examples, and best practices to help you master this topic.

TypeScript enums let you define a set of named constants with automatic numbering, reverse mapping, and runtime presence — offering a structured alternative to magic strings and numbers throughout your codebase.

What You'll Learn

  • Numeric enums and auto-incrementing
  • String enums and their advantages
  • const enums for zero-cost abstraction
  • Reverse mapping in numeric enums
  • Enum vs union types — which to choose

Why It Matters

Spreadsheet-style constants — STATUS_ACTIVE = 1, STATUS_INACTIVE = 2 — are error-prone and unreadable. Enums give them proper names, autocompletion, and type safety. They also serve as a bridge between TypeScript and runtime JavaScript, since enum objects exist at runtime (unlike types, which are erased).

Real-World Use

The Durga Antivirus Pro threat classification system uses a string enum for threat categories: ThreatCategory.Malware, ThreatCategory.Ransomware, etc. When a new scan result comes in, the enum ensures only valid categories are stored, displayed, and queried. The typed API prevents "category ransmware" (a typo) from ever entering the database.

Learning Path

flowchart LR
  A[Functions] --> B[Enums]
  B --> C[Type Assertions]
  B --> D[You Are Here]
  C --> E[Generics Basics]
  E --> F[Advanced Types]

Numeric Enums

By default, enums are numeric, starting from 0:

enum Direction {
  Up,    // 0
  Down,  // 1
  Left,  // 2
  Right, // 3
}

const dir = Direction.Up;
console.log(dir); // 0
console.log(Direction[0]); // "Up" — reverse mapping

Custom Starting Values

enum StatusCode {
  OK = 200,
  Created = 201,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404,
  InternalServerError = 500,
}

console.log(StatusCode.NotFound); // 404
console.log(StatusCode[500]);     // "InternalServerError"

Auto-Incrementing After a Start Value

enum ErrorCode {
  Generic = 1000,
  Network,     // 1001
  Auth,        // 1002
  Validation,  // 1003
}

console.log(ErrorCode.Validation); // 1003

String Enums

Each member must be initialized with a string literal:

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

const color: Color = Color.Red;
console.log(color); // "RED"

function paint(car: Color): void {
  console.log(`Painting car ${car}`);
}

paint(Color.Blue); // Painting car BLUE

String enums don't have reverse mapping — you can't look up "RED" to get Color.Red.

Why String Enums?

// No enum — fragile, no autocompletion
function handleStatus(status: string) {
  if (status === "active") { /* ... */ }
}
handleStatus("actve"); // Typo — no error

// With enum — type-safe
enum Status {
  Active = "active",
  Inactive = "inactive",
  Pending = "pending",
}

function handleStatus(status: Status) { /* ... */ }
handleStatus(Status.Active); // OK
handleStatus("actve");       // Error

String enums are particularly useful when interfacing with APIs that expect specific string values.

Const Enums

Regular enums generate JavaScript code at runtime. const enums are inlined during compilation:

const enum HTTPMethod {
  GET = "GET",
  POST = "POST",
  PUT = "PUT",
  DELETE = "DELETE",
}

const method = HTTPMethod.GET;
// Compiles to: const method = "GET"; (no enum object)

Advantage: Zero runtime overhead. The enum is erased and members are inlined.

Disadvantage: You cannot use reverse mapping or iterate over a const enum. Also, if you publish a library with const enums, you must either use preserveConstEnums or document that consumers should not use isolatedModules (which breaks const enums).

Reverse Mapping

Only numeric enums have reverse mapping:

enum Status {
  Active = 1,
  Inactive = 2,
}

// Compiled JavaScript looks like:
// { 1: "Active", 2: "Inactive", Active: 1, Inactive: 2 }

const nameOfActive = Status[1];  // "Active"
const valueOfActive = Status.Active; // 1

This is useful for deserializing API responses:

enum Role {
  Admin = "admin",
  User = "user",
  Guest = "guest",
}

// API returns: { role: "admin" }
function parseRole(value: string): Role | undefined {
  return Object.values(Role).includes(value as Role)
    ? (value as Role)
    : undefined;
}

Enum vs Union Types

A common design decision: when should you use an enum versus a union of literal types?

// Option A: Enum
enum Status { Active = "active", Inactive = "inactive" }

// Option B: Union type
type Status = "active" | "inactive";
Consideration Enum Union
Runtime object Yes (can iterate) No (compile-time only)
Tree-shakable No (unless const) Yes (inlined)
Autocompletion Yes Yes
Type safety Full Full
Reverse mapping Numeric only No
File size Adds bytes Zero cost
Pattern matching Must import enum Works with literals

Rule of thumb: Use a union when the values are just a small set of strings and you don't need runtime enumeration. Use an enum when you need runtime iteration, reverse mapping, or a clear namespace for related constants.

Common Mistakes

1. Assuming Const Enums Are Always Better

Const enums break with isolatedModules: true (used by many bundlers). If you're building a library, prefer regular enums or unions.

2. Expecting Reverse Mapping on String Enums

enum Color { Red = "RED" }
// Color["RED"]; // Error: no reverse mapping

3. Using Enums When a Union Would Suffice

// Overkill
enum Weekday { Mon, Tue, Wed, Thu, Fri }
const day: Weekday = Weekday.Mon;

// Simpler
type Weekday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri";
const day: Weekday = "Mon";

4. Comparing Enum Members with Loose Equality

enum Status { Active = 1 }

// Bad: compares with raw number
if (someValue == 1) { }

// Good: compare with enum member
if (someValue === Status.Active) { }

5. Mutating Enum Values at Runtime

Enum objects are mutable at runtime. Don't reassign them — it defeats the purpose:

Status.Active = 99; // Works at runtime but should never be done

6. Not Using const for String Enum Object References

// If you only need the values, const enum inlines them:
const enum Color { Red = "#ff0000" }
const hex = Color.Red; // Compiles to: const hex = "#ff0000";

Practice Questions

  1. What value does Direction.Up have in a numeric enum? 0 (the default starting value).

  2. Do string enums support reverse mapping? No. Only numeric enums generate reverse mapping.

  3. What is the difference between const enum and regular enum? const enum members are inlined at compile time with no runtime object. Regular enums generate a runtime object with both forward and reverse mappings.

  4. When should you prefer a union type over an enum? When you only need compile-time type safety without runtime iteration, and when you want to keep the bundle size small.

Challenge: Define an enum for log levels (DEBUG, INFO, WARN, ERROR). Create a function that takes a log level and a message, and only logs messages at or above a configurable threshold level. Use numeric enum values for comparison.

FAQ

Are enums in TypeScript the same as enums in other languages (C#, Java)?

Similar concept, but TypeScript enums are structural (not nominal) and compile to plain JavaScript objects. C# enums are value types backed by integers.

Can I use a computed value as an enum member?

Yes, but only with numeric enums. Members after a computed value must have explicit initializers.

Can I iterate over all values of an enum?

Use Object.values(MyEnum) for string enums. For numeric enums, results include both names and values due to reverse mapping.

Do const enums work with `isolatedModules: true`?

No. const enums are not compatible with isolatedModules. Use regular enums or unions instead.

Can I use TypeScript enums in a React project?

Yes. String enums work well with component props and Redux action types.

Mini Project: HTTP Request Builder

// src/http-enum.ts

enum HTTPMethod {
  GET = "GET",
  POST = "POST",
  PUT = "PUT",
  DELETE = "DELETE",
  PATCH = "PATCH",
}

enum ContentType {
  JSON = "application/json",
  Form = "application/x-www-form-urlencoded",
  Multipart = "multipart/form-data",
  Plain = "text/plain",
}

enum HTTPStatus {
  OK = 200,
  Created = 201,
  NoContent = 204,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404,
  InternalServerError = 500,
}

interface RequestConfig {
  method: HTTPMethod;
  url: string;
  headers?: Record<string, string>;
  body?: unknown;
}

function makeRequest(config: RequestConfig): Promise<{ status: HTTPStatus; data: unknown }> {
  const options: RequestInit = {
    method: config.method,
    headers: {
      "Content-Type": ContentType.JSON,
      ...config.headers,
    },
    body: config.body ? JSON.stringify(config.body) : undefined,
  };

  return fetch(config.url, options).then(async (res) => ({
    status: res.status as HTTPStatus,
    data: await res.json(),
  }));
}

async function main() {
  const response = await makeRequest({
    method: HTTPMethod.GET,
    url: "https://api.dodatech.com/scans",
  });

  if (response.status === HTTPStatus.OK) {
    console.log("Scan data retrieved:", response.data);
  } else if (response.status === HTTPStatus.Unauthorized) {
    console.error("Authentication required");
  }
}

console.log("Available methods:", Object.values(HTTPMethod));
// ["GET", "POST", "PUT", "DELETE", "PATCH"]

console.log("Status 200 means:", HTTPStatus[200]);
// "OK"

What's Next

Now learn about type assertions for when you know more than TypeScript:

Lesson Description
{{< ref "/programming-languages/typescript/06-functions" >}} Review function typing
{{< ref "/programming-languages/typescript/08-type-assertions" >}} The as keyword, type guards, non-null assertion
{{< ref "/programming-languages/typescript/09-generics-basics" >}} Generic functions and type parameters

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro