Back to Blog
TypeScriptEnumsTypesConstants

TypeScript Enums Guide

Master TypeScript enums for type-safe constants and enumerated values.

B
Bootspring Team
Engineering
September 29, 2018
6 min read

Enums allow you to define a set of named constants. Here's how to use them effectively in TypeScript.

Numeric Enums#

1// Basic numeric enum 2enum Direction { 3 Up, // 0 4 Down, // 1 5 Left, // 2 6 Right // 3 7} 8 9// Usage 10const move = Direction.Up; 11console.log(move); // 0 12 13// Custom starting value 14enum Status { 15 Pending = 1, 16 Active, // 2 17 Completed, // 3 18 Cancelled // 4 19} 20 21// Custom values 22enum HttpStatus { 23 Ok = 200, 24 Created = 201, 25 BadRequest = 400, 26 NotFound = 404, 27 ServerError = 500 28} 29 30function handleResponse(status: HttpStatus) { 31 if (status === HttpStatus.Ok) { 32 console.log('Success'); 33 } 34}

String Enums#

1// String enums - more readable at runtime 2enum Color { 3 Red = 'RED', 4 Green = 'GREEN', 5 Blue = 'BLUE' 6} 7 8console.log(Color.Red); // 'RED' 9 10// Better for debugging and serialization 11enum UserRole { 12 Admin = 'admin', 13 User = 'user', 14 Guest = 'guest' 15} 16 17function setRole(role: UserRole) { 18 console.log(`Setting role to: ${role}`); 19} 20 21setRole(UserRole.Admin); // 'Setting role to: admin' 22 23// API response matching 24enum OrderStatus { 25 Pending = 'pending', 26 Processing = 'processing', 27 Shipped = 'shipped', 28 Delivered = 'delivered' 29}

Heterogeneous Enums#

1// Mixed types (not recommended) 2enum Mixed { 3 No = 0, 4 Yes = 'YES' 5} 6 7// Prefer consistent types 8enum BooleanLike { 9 False = 0, 10 True = 1 11} 12 13// Or use string for all 14enum Answer { 15 No = 'no', 16 Yes = 'yes' 17}

Computed Enums#

1// Computed values 2enum FileAccess { 3 None, 4 Read = 1 << 1, // 2 5 Write = 1 << 2, // 4 6 ReadWrite = Read | Write, // 6 7 Admin = ReadWrite | 1 << 3 // 14 8} 9 10// Using functions (must be const-evaluable) 11const getValue = () => 100; 12 13enum Computed { 14 A = getValue(), // Works if getValue is const 15 B, // Error if A is not const-evaluable 16} 17 18// Safe computed enum 19enum Permissions { 20 None = 0, 21 Read = 1, 22 Write = 2, 23 Execute = 4, 24 All = Read | Write | Execute // 7 25}

Const Enums#

1// const enum - inlined at compile time 2const enum Direction { 3 Up, 4 Down, 5 Left, 6 Right 7} 8 9const dir = Direction.Up; 10// Compiles to: const dir = 0; 11 12// Benefits: 13// - No runtime overhead 14// - Smaller bundle size 15// - Better performance 16 17// Limitations: 18// - Can't use reverse mapping 19// - Can't iterate over values 20// - Must use preserveConstEnums for some use cases 21 22// Regular enum generates object 23enum RegularEnum { 24 A, B, C 25} 26// Compiles to: var RegularEnum = { A: 0, B: 1, C: 2, 0: "A", 1: "B", 2: "C" } 27 28// const enum is inlined 29const enum ConstEnum { 30 A, B, C 31} 32// Usage is replaced with literal values

Reverse Mapping#

1// Numeric enums have reverse mapping 2enum Status { 3 Active, 4 Inactive 5} 6 7// Forward: name to value 8const activeValue = Status.Active; // 0 9 10// Reverse: value to name 11const activeName = Status[0]; // 'Active' 12 13// Useful for debugging 14function getStatusName(status: Status): string { 15 return Status[status]; 16} 17 18// String enums DON'T have reverse mapping 19enum Color { 20 Red = 'RED' 21} 22 23// Color['RED'] is undefined 24// Only Color.Red works

Enum as Types#

1// Enum creates both value and type 2enum UserType { 3 Admin = 'admin', 4 User = 'user' 5} 6 7// Use as type 8function createUser(type: UserType): void { 9 console.log(type); 10} 11 12// Must use enum member 13createUser(UserType.Admin); // OK 14createUser('admin'); // Error in strict mode 15 16// Union of enum values 17type UserTypeValue = `${UserType}`; 18// 'admin' | 'user'

Enum Iteration#

1// Iterate over numeric enum 2enum Direction { 3 Up, 4 Down, 5 Left, 6 Right 7} 8 9// Get all values 10const directions = Object.values(Direction) 11 .filter(v => typeof v === 'number'); 12// [0, 1, 2, 3] 13 14// Get all names 15const directionNames = Object.keys(Direction) 16 .filter(k => isNaN(Number(k))); 17// ['Up', 'Down', 'Left', 'Right'] 18 19// String enum - simpler 20enum Color { 21 Red = 'red', 22 Green = 'green', 23 Blue = 'blue' 24} 25 26const colors = Object.values(Color); 27// ['red', 'green', 'blue'] 28 29const colorNames = Object.keys(Color); 30// ['Red', 'Green', 'Blue']

Enums vs Union Types#

1// Union type alternative 2type Direction = 'up' | 'down' | 'left' | 'right'; 3 4const move: Direction = 'up'; 5 6// Benefits over enum: 7// - No runtime code 8// - Works with string literals 9// - Better tree-shaking 10 11// Enum benefits: 12// - Reverse mapping 13// - Iteration 14// - Namespace for related values 15// - IDE autocomplete 16 17// as const object (best of both) 18const Direction = { 19 Up: 'up', 20 Down: 'down', 21 Left: 'left', 22 Right: 'right' 23} as const; 24 25type Direction = typeof Direction[keyof typeof Direction]; 26// 'up' | 'down' | 'left' | 'right' 27 28// Usage 29const dir: Direction = Direction.Up;

Enum Patterns#

1// Enum with associated data 2enum LogLevel { 3 Error = 'error', 4 Warn = 'warn', 5 Info = 'info', 6 Debug = 'debug' 7} 8 9const logPriority: Record<LogLevel, number> = { 10 [LogLevel.Error]: 0, 11 [LogLevel.Warn]: 1, 12 [LogLevel.Info]: 2, 13 [LogLevel.Debug]: 3 14}; 15 16// Enum with methods (namespace) 17enum Color { 18 Red = '#ff0000', 19 Green = '#00ff00', 20 Blue = '#0000ff' 21} 22 23namespace Color { 24 export function toRgb(color: Color): [number, number, number] { 25 const hex = color.slice(1); 26 return [ 27 parseInt(hex.slice(0, 2), 16), 28 parseInt(hex.slice(2, 4), 16), 29 parseInt(hex.slice(4, 6), 16) 30 ]; 31 } 32} 33 34const rgb = Color.toRgb(Color.Red); // [255, 0, 0]

Enum Type Guards#

1enum Status { 2 Active = 'active', 3 Inactive = 'inactive' 4} 5 6// Check if value is valid enum member 7function isStatus(value: string): value is Status { 8 return Object.values(Status).includes(value as Status); 9} 10 11// Usage 12const input = 'active'; 13if (isStatus(input)) { 14 // input is Status 15 handleStatus(input); 16} 17 18// Generic enum guard 19function isEnumValue<T extends Record<string, string>>( 20 enumObj: T, 21 value: string 22): value is T[keyof T] { 23 return Object.values(enumObj).includes(value); 24} 25 26if (isEnumValue(Status, userInput)) { 27 // userInput is Status 28}

Common Use Cases#

1// API status codes 2enum ApiStatus { 3 Loading = 'loading', 4 Success = 'success', 5 Error = 'error' 6} 7 8// Feature flags 9enum Feature { 10 DarkMode = 'dark_mode', 11 BetaFeatures = 'beta_features', 12 Analytics = 'analytics' 13} 14 15// Event types 16enum EventType { 17 Click = 'click', 18 Submit = 'submit', 19 Change = 'change' 20} 21 22// State machine states 23enum OrderState { 24 Draft = 'draft', 25 Submitted = 'submitted', 26 Processing = 'processing', 27 Completed = 'completed', 28 Cancelled = 'cancelled' 29}

Best Practices#

When to Use Enums: ✓ Fixed set of related constants ✓ Need reverse mapping ✓ IDE autocomplete benefits ✓ Namespace for values Prefer const enum: ✓ When you don't need runtime object ✓ For performance-critical code ✓ When values are simple Prefer Union Types: ✓ Simple string literals ✓ No need for iteration ✓ Better tree-shaking needed ✓ Working with APIs Avoid: ✗ Heterogeneous enums ✗ Complex computed values ✗ Enums for single values ✗ Overusing numeric enums

Conclusion#

TypeScript enums provide type-safe constants with IDE support and optional runtime features. Use string enums for readability, const enums for performance, and consider union types or as const objects as alternatives. Choose based on whether you need runtime iteration, reverse mapping, or minimal bundle size.

Share this article

Help spread the word about Bootspring