TypeScript Enums vs Const Enums vs Union Types
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list TypeScript compiler and ESLint as tools. The TypeScript compiler won't flag regular enum usage as wrong (it's valid syntax), but ESLint with TypeScript-specific rules (e.g., @typescript-eslint/prefer-string-literal-type or no-enum rules) can catch cases where union types or const enums would be preferable. This is specialist tooling that requires deliberate configuration, not a default lint rule.
Closest to 'simple parameterised fix' (e3). The quick_fix indicates preferring const enums or union types over regular enums. Replacing a regular enum with a union type or const enum is a mechanical substitution — update the declaration and update call sites within a single file or component. It's more than a one-liner (all usages must be updated) but doesn't span the whole codebase unless the enum is widely shared.
Closest to 'localised tax' (b3). The applies_to covers web and CLI contexts broadly, but enums are typically defined in one place and their burden is felt mainly in that component or module. If the enum leaks into APIs (numeric enums in JSON), the pain is more widespread, but in a typical project the structural debt is localised to the module using the enum rather than architectural in nature.
Closest to 'serious trap' (t7). The misconception field directly states that developers believe enums are always better than union types because they are explicit — yet string union types are fully type-safe, produce no JavaScript, and are the idiomatic TypeScript approach. Additionally, numeric enums allow Status[99] at runtime with no error, which contradicts the expectation that TypeScript's type system would catch invalid values. This contradicts how similar constructs work in other typed languages and how developers expect type safety to behave, landing solidly at t7.
Also Known As
TL;DR
Explanation
Regular enum: generates JavaScript object, supports reverse mapping (Direction[0] === 'Up'). Const enum: fully inlined at compile time — zero JavaScript generated, faster but no runtime access. String union type: type Direction = 'Up' | 'Down' — no JavaScript emitted, readable, tree-shakeable, and more compatible with JSON APIs. The TypeScript community trend: prefer string union types over enums for most cases; use const enums only when performance is critical and runtime access is not needed; avoid regular numeric enums (they allow invalid values).
Common Misconception
Why It Matters
Common Mistakes
- Numeric enums in APIs — JSON serialises as numbers, not readable names.
- Regular enums where const enums or union types would be better — unnecessary JavaScript overhead.
- Mixing enum and non-enum values — direction: Direction | null requires careful handling.
- Using enum as a namespace — use a regular object const as a namespace instead.
Code Examples
// Numeric enum — allows invalid values, generates JS:
enum Direction { Up, Down, Left, Right }
const d: Direction = 99; // No error! 99 is not a valid direction
JSON.stringify(Direction.Up); // '0' — not readable
// Regular enum generates runtime JS object:
// var Direction;
// Direction[Direction.Up = 0] = 'Up'; ...
// String union — zero JS, type-safe, readable JSON:
type Direction = 'Up' | 'Down' | 'Left' | 'Right';
const d: Direction = 'Invalid'; // Compile error!
JSON.stringify('Up'); // 'Up' — readable
// Const enum — inlined, no JS:
const enum Color { Red = 'red', Blue = 'blue' }
const c = Color.Red; // Compiled to: const c = 'red'
// Object const as namespace when runtime needed:
export const Direction = { Up: 'up', Down: 'down' } as const;
export type Direction = typeof Direction[keyof typeof Direction];