TypeScript Enums

Enums, short for "enumerations," are a powerful feature in TypeScript that allow you to define a collection of related constants under a single, meaningful name. Instead of using scattered strings or numbers (often called "magic values") throughout your codebase, enums provide a structured way to manage sets of options, making your code more readable, maintainable, and type-safe.

 

Why Use Enums?

  • Organized Constants: They group related values together, such as status codes, directions, or user roles.
  • Readability: if (status === Status.Active) is much easier to understand at a glance than if (status === 1).
  • Refactoring Safety: If you need to change a value, you only change it in one place (the enum definition) rather than searching and replacing strings throughout your project.
  • Flexibility: TypeScript supports numeric, string, and even mixed (heterogeneous) enums to suit different architectural needs.
Best Practice: Use enums when you have a fixed set of known values at compile time, such as days of the week, HTTP methods, or application states.

 

Numeric Enums

Numeric enums are the default in TypeScript. If you don't assign a value to the members, TypeScript automatically assigns them numbers starting from 0 and increments them by one for each subsequent member.

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

console.log(Direction.Up); // Output: 0  

You can also initialize the first member with a specific number, and the rest will auto-increment from that starting point:

enum Direction {  
  Up = 10,  
  Down,  // 11
  Left,  // 12
  Right  // 13
}  

console.log(Direction.Down); // Output: 11
Common Mistake: Beginners often assume numeric enums are strictly type-safe. However, TypeScript allows you to assign any number to a numeric enum variable, which can lead to runtime bugs if not handled carefully.

String Enums

In string enums, every member must be explicitly initialized with a string literal. These are incredibly popular because they offer "meaningful" values during debugging and when inspecting data sent over a network.

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

// Real-world usage: checking API response status
function handleResponse(status: Status) {
  if (status === Status.Success) {
    console.log("Operation was successful!");
  }
}
Developer Tip: Use string enums for values that need to be logged or sent to an API. Seeing "SUCCESS" in your database or logs is much more helpful than seeing the number 0.

Heterogeneous Enums

Technically, TypeScript allows enums to contain both strings and numbers. While this exists, it is rarely used in professional production environments because it creates confusion and makes the code harder to reason about.

enum MixedEnum {  
  Success = "SUCCESS",  
  Error = 500,  
  Timeout = 408  
}  

console.log(MixedEnum.Success); // Output: "SUCCESS"  
console.log(MixedEnum.Error);   // Output: 500  
Watch Out: Avoid heterogeneous enums unless you are dealing with a very specific legacy API. Stick to either all-numeric or all-string enums for consistency.

Reverse Mapping

Numeric enums feature "reverse mapping," which allows you to look up the name of an enum member if you only have its numeric value. This is possible because TypeScript compiles numeric enums into an object that maps both ways.

enum Color {  
  Red = 1,  
  Green,  
  Blue  
}  

let greenValue = Color.Green; // 2
console.log(Color[greenValue]); // Output: "Green"

 

Note: Reverse mapping does not work for string enums. String enums only map from Name to Value.

 

Const Enums

Standard enums generate code in your JavaScript output. If you want to avoid this overhead, you can use const enums. During compilation, TypeScript will remove the enum object entirely and "inline" the values wherever they are used, resulting in smaller and faster code.

const enum Size {  
  Small = 1,  
  Medium = 2,  
  Large = 3  
}  

// This becomes: console.log(1); in compiled JavaScript
console.log(Size.Small); 
Developer Tip: Use const enum when you want the best performance and don't need the ability to look up values at runtime or use reverse mapping.

Enum with Functions

Enums are excellent for defining function parameters. They ensure that whoever calls your function provides a valid value from your predefined set, preventing invalid data from entering your logic.

enum Days {  
  Monday,  
  Tuesday,  
  Wednesday,  
  Thursday,  
  Friday,
  Saturday,
  Sunday
}  

function isWorkingDay(day: Days): boolean {  
  // Logic to determine if it's a weekday
  return day !== Days.Saturday && day !== Days.Sunday;  
}  

console.log(isWorkingDay(Days.Monday)); // Output: true  

Enums as Object Keys

Enums can serve as keys for objects, which is a great way to map specific enum values to configuration settings or UI labels.

enum UserRole {  
  Admin = "ADMIN",  
  User = "USER",
  Guest = "GUEST"
}  

const RolePermissions: Record<UserRole, string> = {  
  [UserRole.Admin]: "Can delete and edit all content",  
  [UserRole.User]: "Can create and edit own content",
  [UserRole.Guest]: "Read-only access"
};  

console.log(RolePermissions[UserRole.Admin]); // Output: Full Access

 

Summary

TypeScript enums are a fundamental tool for writing clean, self-documenting code. By using Numeric Enums for simple flagging, String Enums for readable data and APIs, and Const Enums for performance optimizations, you can significantly reduce "magic values" in your projects. While modern TypeScript developers often debate between using Enums and Union Types, Enums remain a robust choice when you need a clear, centralized collection of constants with built-in namespacing.