Suggest an editImprove this articleRefine the answer for “Why is enum often considered an antipattern in TS?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)`enum` is often called an antipattern because it is "runtime magic" that generates real JavaScript code, works poorly with tree-shaking and APIs/JSON, and does not always guarantee type safety. **Key point:** an `as const` object or a union of string literals is recommended instead - neither generates extra code, and both integrate better with the JS ecosystem.Shown above the full answer for quick recall.Answer (EN)Image## In short: why `enum` is often called an antipattern > Because `enum` is **"runtime magic"** > that **hurts type safety**, > **adds bulk to the code**, and **integrates worse with the JS ecosystem** > than alternatives like `const enum` or `as const` objects. --- ## 1. `enum` exists both at runtime and at compile time The main feature (and problem) of `enum` is that it **generates JavaScript code** at compile time. Example: ```javascript enum Direction { Up, Down, Left, Right, } ``` Compiles to JS as: ```javascript "use strict"; var Direction; (function (Direction) { Direction[(Direction["Up"] = 0)] = "Up"; Direction[(Direction["Down"] = 1)] = "Down"; Direction[(Direction["Left"] = 2)] = "Left"; Direction[(Direction["Right"] = 3)] = "Right"; })(Direction || (Direction = {})); ``` That is, `enum` is **not a purely type-level construct**, it **generates a real object**, which: - adds extra code to the bundle, - can behave differently than you'd expect. --- ## 2. A two-way mapping (reverse mapping) = a potential bug TypeScript creates a two-way table: ```javascript Direction.Up === 0 Direction[0] === "Up" ``` This is convenient, but dangerous: ```javascript enum Role { Admin, User } const role = Role[0]; // "Admin" ``` Values can be **reversed**, which creates **hidden vulnerabilities and confusion**. --- ## 3. Enum values are not type-safe An enum behaves **almost like an object with** `number | string` **values**, and TypeScript **often does not protect** against assignment errors. Example: ```javascript enum Role { Admin, User } const role: Role = 42; // allowed (!) ``` This is not obvious and **breaks** the point of an enum, because `42` is not an existing value, but TS does not filter it out. --- ## 4. Mixing types (numeric + string enums) You can write this: ```javascript enum Mixed { Yes = "YES", No = 0, } ``` Combining numbers and strings like this leads to **unpredictable behavior** and makes type checking harder. --- ## 5. `enum` does not work well with tree-shaking Since `enum` compiles to a real JS object, it **cannot be removed** by the bundler's optimizer (for example, Terser or esbuild). Example: ```javascript enum Colors { Red, Blue, Green } ``` Even if `Colors` is not used, it **still ends up in the bundle**. But an object with `as const` **is removed** if it is not used: ```javascript const Colors = { Red: 0, Blue: 1, Green: 2 } as const; ``` --- ## 6. Poor compatibility with plain JS / JSON / APIs An enum is a runtime object, and its values do not match plain strings. Example: ```javascript enum Status { Active = "active", Inactive = "inactive" } function setStatus(status: Status) { ... } setStatus("active"); // Error - TS expects Status.Active ``` Although the string `"active"` **matches in meaning**, TypeScript requires exactly `Status.Active`. This gets in the way of using an enum with data from an API. It is better to use a string literal: ```javascript type Status = "active" | "inactive"; ``` --- ## 7. Difficulties with serialization and debugging ```javascript enum Status { Active, Inactive } JSON.stringify(Status); // {"0":"Active","1":"Inactive","Active":0,"Inactive":1} ``` Instead of a simple enumeration, you get a useless jumble that's hard to pull the needed values out of. --- ## 8. Enum behavior breaks the "value-level vs type-level" concept TypeScript normally separates: - **type level** (only at compile time) - **value level** (at runtime) `enum` *mixes these two levels*. Example: ```javascript enum Fruit { Apple, Orange } function eat(fruit: Fruit) { ... } const a = Fruit.Apple; // a variable and a type in one ``` This is "magic" that breaks TS's predictability principles. --- ## 9. An alternative: `as const` + `typeof` A more modern and safer approach is **literal objects with** `as const`. Example: ```javascript const Direction = { Up: "up", Down: "down", Left: "left", Right: "right", } as const; type Direction = typeof Direction[keyof typeof Direction]; ``` Advantages: - Generates no JS code (only types); - Works with APIs and JSON; - Tree-shaking friendly; - Does not break typing. --- ## 10. `const enum` - a compromise (but with its own nuances too) If you still need an enum, it's better to use `const enum`: ```javascript const enum Direction { Up, Down, Left, Right, } ``` TypeScript **inlines** the values directly: ```javascript const move = Direction.Up; // compiles to: const move = 0; ``` But: - it does not work with `isolatedModules: true` (for example, with Babel); - it breaks debugging (values turn into numbers); - it can be incompatible with other tools (for example, SWC, ts-node). --- ## 11. `enum` breaks the idea of "a type as a contract document" For example, when you use `enum` in a public API, consumers see numeric codes rather than literal values. This makes the type less self-documenting. Example: ```javascript enum Status { Success, Fail } function getStatus(): Status { ... } ``` Returns `0 | 1`, but the IDE will not hint `"Success" | "Fail"` - understanding the code becomes harder. --- ## 12. Summary: when `enum` is actually needed `enum` is justified **only in rare cases**: - for compatibility with existing C#/Java code; - when working with external libraries where an enum is part of the API; - in specific compile-time scenarios (`const enum`). For everything else: **use a string literal union or an** `as const` **object.** --- ## Final comparison table | Approach | Generates code? | Safe? | Convenient with APIs? | Tree-shaking | Recommended | |---|---|---|---|---|---| | `enum` | Yes | Partially | No | No | No | | `const enum` | No (inline) | Yes | Partially | Yes | With caution | | `as const` object | No | Yes | Yes | Yes | Recommended | | A union of types (`'a' | 'b'`) | No | Yes | Yes | Yes |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.