Understanding TypeScript Enums: Regular vs. Const Enums

Understanding TypeScript Enums: Regular vs. Const Enums
Photo by Joris Beugels / Unsplash

Enums in TypeScript are a powerful feature that allow you to define a set of named constants. They make your code more expressive and easier to read by replacing hard-coded values with meaningful names. However, TypeScript provides two types of enums: regular enums and const enums. Understanding the differences between them is key to making the right choice for your project.

What Are Enums in TypeScript?

At their core, enums allow you to define a collection of related values that can be numeric or string-based. For example:

enum Direction {
    Up,
    Down,
    Left,
    Right
}

Here, Direction.Up has a default value of 0, Direction.Down is 1, and so on. This default behavior is called numeric enum auto-incrementing.

Alternatively, enums can have explicit values:

enum Status {
    Success = "SUCCESS",
    Failure = "FAILURE",
    Pending = "PENDING"
}

Now that we understand what enums are, let’s dive into the differences between regular enums and const enums.

Regular Enums: A Runtime Object

Regular enums compile into a JavaScript object that exists at runtime. This makes them dynamic and versatile but can lead to slightly larger output.

Key Features of Regular Enums:

  1. Runtime Object: Regular enums generate an object in the compiled JavaScript code, making them accessible at runtime.
  2. Reverse Mapping: These enums allow bidirectional mapping, meaning you can look up the name of an enum using its value and vice versa.

Example:

enum Direction {
    Up,
    Down,
    Left,
    Right
}

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

Compiled JavaScript:

var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
    Direction[Direction["Left"] = 2] = "Left";
    Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));

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

This two-way mapping is a unique feature of regular enums, but it comes at the cost of slightly increased bundle size and runtime overhead.

When to Use Regular Enums:

  • When you need reverse mapping.
  • If your application requires enums to exist as objects at runtime.
  • When working with dynamic or runtime-driven scenarios, such as iterating over enum values.

Const Enums: Efficient and Inlined

const enums are a more performance-focused version of enums. Instead of creating a runtime object, const enums are inlined directly into the compiled JavaScript.

Key Features of Const Enums:

  1. No Runtime Object: The compiler removes const enums entirely, replacing their usage with the corresponding values.
  2. No Reverse Mapping: Unlike regular enums, you cannot look up a name using a value.

Example:

const enum Direction {
    Up,
    Down,
    Left,
    Right
}

const move = Direction.Up;
console.log(move); // Outputs: 0

Compiled JavaScript:

const move = 0;
console.log(move); // 0

Notice how the enum was completely removed from the compiled code, leaving only the inlined value.

When to Use Const Enums:

  • When performance and bundle size are critical.
  • If you don’t need reverse mapping or runtime enum objects.
  • For static and predictable use cases, such as configuration constants.

Additional Considerations

String Enums

Both regular and const enums support string values, but only regular enums offer reverse mapping for numeric values. For example:

enum Status {
    Success = "SUCCESS",
    Failure = "FAILURE"
}

String enums do not support reverse mapping, regardless of whether they are regular or const.

Mixed Enums

Enums can mix string and numeric values, but this is generally discouraged for clarity and consistency.

enum Mixed {
    Yes = 1,
    No = "NO"
}

Namespace and Module Compatibility

Regular enums integrate seamlessly with TypeScript namespaces, while const enums do not.

Debugging

Regular enums are easier to debug because they exist as objects in the runtime, making their structure inspectable during debugging. Const enums, being inlined, lack this advantage.

Compiler Flags

Using const enums may require specific compiler settings. The --preserveConstEnums flag can ensure that const enums remain in the output for certain use cases, but by default, they are removed.

Summary

Here’s a quick comparison of the two types of enums:

Feature Regular Enum Const Enum
Runtime Object Yes No
Reverse Mapping Yes No
Inlining No Yes
Debugging Ease Easier Harder
Performance Slower Faster
Use in Dynamic Scenarios Yes No

Choosing the Right Enum

  • Use regular enums when you need runtime features, such as reverse mapping or dynamic usage.
  • Use const enums for performance-critical scenarios where runtime access to the enum is unnecessary.

By understanding these differences, you can make informed decisions to optimize both your code’s performance and readability.

Finally

For many projects, a mix of regular and const enums might be appropriate. Use each type where its strengths align with your specific needs!

Support Us