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 valuesReverse 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 worksEnum 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.