TypeScript Enum vs Union Types Explained with Examples

Learn the key differences between TypeScript enums and union types with clear explanations and practical examples. Understand when and how to use each in your code.

If you're new to TypeScript or exploring different ways to define a set of fixed values, you might have encountered both enums and union types. Both serve a similar purpose: restricting variables to a limited set of values. Understanding the differences between them and when to use each can make your code more readable, maintainable, and even safer.

TypeScript enums are a special feature that allow you to define a named set of constants. They create an object with both keys and values at runtime, which makes them useful when you need a meaningful representation for a set of options. On the other hand, union types are a compile-time-only construct that allows you to specify that a variable can be one of several literal types, usually string or number literals. Union types do not exist in the compiled JavaScript output.

typescript
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

// Usage of Enum
function moveEnum(direction: Direction) {
  console.log(`Moving: ${direction}`);
}

moveEnum(Direction.Up); // Output: Moving: UP

// Using Union Types
type DirectionUnion = "UP" | "DOWN" | "LEFT" | "RIGHT";

function moveUnion(direction: DirectionUnion) {
  console.log(`Moving: ${direction}`);
}

moveUnion("LEFT"); // Output: Moving: LEFT

To use enums correctly, you import or declare the enum and then refer to its members with dot notation. This helps reduce typos since you’re accessing predefined named values. Union types are best when you want to keep your code lightweight and don’t need the runtime object that enums produce. With unions, you simply use strings or numbers directly, relying on TypeScript's type checking to prevent invalid assignments.

A common mistake beginners make is confusing enum members with their string values. For example, it's easy to accidentally pass a plain string instead of an enum member, which might not be caught if the enum is not used strictly. Another error is overusing enums when simple union types would suffice, which can add unnecessary complexity or runtime overhead if your project is targeting smaller bundles or strict tree-shaking.

In summary, enums and union types are both powerful ways to restrict possible values in TypeScript. Enums provide a convenient, named object at runtime and are useful if you need to reuse values across your code or access them dynamically. Union types are simpler, more lightweight options perfectly suited for defining specific sets of valid literals without adding extra runtime code. Choosing between them depends on your use case, project size, and preference for runtime features versus compile-time checks.