Enums
Named constants with enums — and modern alternatives using const objects and union types.
Numeric and String Enums
Numeric enums auto-increment from 0 unless specified: `enum Status { Pending, Active }` maps to 0, 1. String enums require explicit values: `enum Role { Admin = "ADMIN", User = "USER" }`.
String enums produce readable runtime values and debug output. Numeric enums compact storage but reverse mapping adds unexpected object keys at runtime.
- Numeric enums auto-increment
- String enums need explicit values
- Enums emit runtime objects
enum OrderStatus {
Pending = 'PENDING',
Shipped = 'SHIPPED',
Delivered = 'DELIVERED',
}Const Enums and Erasure
`const enum` inlines members at compile time with no runtime object — smaller bundles but no runtime iteration over values. Regular enums generate JavaScript objects usable at runtime for dropdowns and validation.
With `isolatedModules`, const enums can cause issues in some bundlers. Many teams avoid const enums entirely and use union types or `as const` objects instead.
- Const enums inline — no runtime object
- Regular enums work for runtime iteration
- Avoid const enums with some bundlers
Modern Alternatives
Union types of string literals provide compile-time safety without runtime cost: `type Status = "pending" | "active"`. Const assertion objects with `as const` generate both runtime values and inferred union types.
`const Status = { Pending: "pending", Active: "active" } as const; type Status = typeof Status[keyof typeof Status]` is the idiomatic modern replacement for string enums in many codebases.
- Union types — zero runtime cost
- `as const` objects for runtime + types
- Preferred in many style guides
const Status = { Pending: 'pending', Active: 'active' } as const;
type Status = (typeof Status)[keyof typeof Status];When Enums Still Make Sense
Enums remain appropriate when you need runtime iteration, bidirectional mapping (numeric), or consistency with Java/C# backends that use enums. GraphQL and some ORMs integrate cleanly with TypeScript enums.
If the team style guide bans enums, use const objects consistently. Mixed approaches in one codebase confuse contributors — pick one pattern per project.
- Enums when runtime iteration needed
- Backend parity with Java/C# enums
- One pattern per codebase
Exhaustiveness with Enums and Unions
Switch over enum or union values with a `default` branch assigning to `never` to catch unhandled cases when new members are added. The compiler errors if a case is missing.
This pattern is critical for reducers, payment status handlers, and permission checks — places where new enum values silently falling through causes production bugs.
- Exhaustive switch with `never` default
- Compiler catches missing cases
- Essential in reducers and status handlers
function handle(s: Status) {
switch (s) {
case Status.Pending: return 'wait';
default: { const _x: never = s; return _x; }
}
}