TypeScript Enums: The Complete Guide
Enums allow you to define a set of named constants. They make code more readable and maintainable.
Numeric Enums
Numeric enums are the default. They auto-increment from 0.
enum Status {
Pending, // 0
Approved, // 1
Rejected // 2
}
function updateStatus(status: Status) {
if (status === Status.Pending) {
console.log("Waiting for approval");
}
}
updateStatus(Status.Pending); // ✅ Type-safe
Custom Numeric Values
enum HttpStatus {
OK = 200,
Created = 201,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
InternalServerError = 500
}
function handleResponse(status: HttpStatus) {
switch (status) {
case HttpStatus.OK:
return "Success!";
case HttpStatus.NotFound:
return "Resource not found";
default:
return "Unknown status";
}
}
console.log(handleResponse(HttpStatus.OK)); // "Success!"
Partial Initialization
enum Priority {
Low = 1, // 1
Medium, // 2 (auto-increments)
High, // 3
Critical = 10, // 10
Urgent // 11 (auto-increments from 10)
}
String Enums
String enums require every member to be initialized with a string literal.
enum LogLevel {
Error = "ERROR",
Warning = "WARNING",
Info = "INFO",
Debug = "DEBUG"
}
function log(level: LogLevel, message: string) {
console.log(`[${level}] ${message}`);
}
log(LogLevel.Error, "Something went wrong");
// Output: [ERROR] Something went wrong
Real-World Example: API Endpoints
enum ApiEndpoint {
Users = "/api/users",
Posts = "/api/posts",
Comments = "/api/comments",
Auth = "/api/auth"
}
async function fetchData(endpoint: ApiEndpoint) {
const response = await fetch(endpoint);
return response.json();
}
// Usage
const users = await fetchData(ApiEndpoint.Users);
const posts = await fetchData(ApiEndpoint.Posts);
Real-World Example: User Roles
enum UserRole {
Admin = "ADMIN",
Editor = "EDITOR",
Viewer = "VIEWER",
Guest = "GUEST"
}
interface User {
id: string;
name: string;
role: UserRole;
}
function hasPermission(user: User, requiredRole: UserRole): boolean {
const roleHierarchy = {
[UserRole.Admin]: 4,
[UserRole.Editor]: 3,
[UserRole.Viewer]: 2,
[UserRole.Guest]: 1
};
return roleHierarchy[user.role] >= roleHierarchy[requiredRole];
}
const user: User = {
id: "1",
name: "Alice",
role: UserRole.Editor
};
console.log(hasPermission(user, UserRole.Viewer)); // true
console.log(hasPermission(user, UserRole.Admin)); // false
Const Enums
Const enums are completely removed during compilation for better performance.
const enum Direction {
Up,
Down,
Left,
Right
}
const move = Direction.Up;
// Compiles to: const move = 0;
// No enum object is generated
When to Use Const Enums
✅ Use for: Performance-critical code ❌ Don't use if: You need reverse mapping
Reverse Mapping
Numeric enums have reverse mapping - get the enum name from its value.
enum Status {
Pending,
Approved,
Rejected
}
console.log(Status[0]); // "Pending"
console.log(Status["Pending"]); // 0
// String enums don't have reverse mapping
enum Color {
Red = "RED",
Green = "GREEN"
}
console.log(Color["Red"]); // "RED"
console.log(Color["RED"]); // undefined (no reverse)
Enums vs Union Types
// Enum
enum Direction {
Up = "UP",
Down = "DOWN"
}
// Union Type (often preferred)
type Direction = "UP" | "DOWN";
// Enums: Better for related constants with behavior
// Unions: Better for simple string literals
Best Practices
1. Use string enums for better debugging 2. Use const enums for performance when you don't need reverse mapping 3. Prefer union types for simple cases 4. Document enum values for clarity
Example: Complete Enum Usage
enum OrderStatus {
Pending = "PENDING",
Processing = "PROCESSING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED"
}
interface Order {
id: string;
status: OrderStatus;
total: number;
}
function canCancelOrder(order: Order): boolean {
return [
OrderStatus.Pending,
OrderStatus.Processing
].includes(order.status);
}
function getStatusMessage(status: OrderStatus): string {
switch (status) {
case OrderStatus.Pending:
return "Order received";
case OrderStatus.Processing:
return "Preparing your order";
case OrderStatus.Shipped:
return "On the way";
case OrderStatus.Delivered:
return "Delivered successfully";
case OrderStatus.Cancelled:
return "Order cancelled";
default:
const _exhaustive: never = status;
throw new Error("Unhandled status");
}
}
Conclusion
Enums are powerful for:
- Defining related constants
- Type-safe value sets
- Better code documentation
- Exhaustive switch statements
Choose wisely between numeric, string, and const enums based on your needs!
Want articles like this in your inbox?
Join developers and founders who get practical insights on frontend, SaaS, and building better products.
Written by Salman Izhar
Frontend Developer specializing in React, Next.js, and building high-converting web applications.
Learn More