const enum vs enum
Главное отличие: обычный
enumкомпилируется в JS-объект (есть в рантайме),const enumинлайнится в значения (нет в рантайме).
Быстрая сравнительная таблица
| Аспект | enum |
const enum |
|---|---|---|
| Runtime-объект | ✅ есть | ❌ нет (инлайн) |
| Размер бандла | + ~50-200 байт на enum | 0 |
| Tree-shaking | ⚠️ работает плохо | ✅ работает идеально |
Reverse mapping (Color[0] → "Red") |
✅ для числовых | ❌ нет |
Object.keys(Enum) / Object.values(Enum) |
✅ работает | ❌ нет |
| Динамическое использование | ✅ работает | ❌ нет |
isolatedModules: true |
✅ работает | ❌ ошибка (Babel, esbuild, swc) |
| Подходит для библиотек | ⚠️ заставляет потребителя тащить enum | ❌ нельзя в .d.ts для внешних потребителей |
Минимальный пример
// Обычный enum
enum Color { Red, Green, Blue }
const c = Color.Red;
// JS: var Color; (function(Color){...})(Color||(Color={})); const c = Color.Red;
// const enum
const enum Color { Red, Green, Blue }
const c = Color.Red;
// JS: const c = 0; ← полностью инлайн, объекта Color нет
Когда использовать const enum
- Чистое приложение (не библиотека)
- Сборщик:
tsc(не Babel/esbuild) илиisolatedModules: false - Не нужен runtime-доступ (итерация, reverse mapping)
- Хочешь нулевой overhead
Когда использовать обычный enum
- Библиотека для внешних потребителей
- Нужен
Object.values(Enum)для итерации - Используется в switch/Map в рантайме
- Сборщик с
isolatedModules: true(Vite, Next.js, Remix, esbuild, swc)
Когда НЕ использовать ни тот ни другой
→ Используй as const объект или union type:
// 1. as const объект — runtime + типы
const Color = { Red: 'red', Green: 'green', Blue: 'blue' } as const;
type Color = (typeof Color)[keyof typeof Color]; // 'red' | 'green' | 'blue'
// 2. Чистый union — нет runtime
type Color = 'red' | 'green' | 'blue';
Преимущества:
- ✅ Идеальный tree-shaking
- ✅ Работает везде (Vite, esbuild, любой bundler)
- ✅ Можно итерировать (
Object.values(Color)) - ✅ Сериализуется в JSON естественно
Главные подводные камни
1. isolatedModules: true + const enum = 💥
Vite, Next.js, esbuild, swc, Babel компилируют файлы изолированно — не видят соседних файлов. const enum требует знания всех значений на этапе компиляции — поэтому не работает.
Симптом: Error: Cannot access ambient const enums when 'isolatedModules' is enabled.
Решение: убрать const, использовать обычный enum или union.
2. Числовой enum принимает любое число
enum Status { Active, Inactive } // 0, 1
function set(s: Status) { /* ... */ }
set(999); // ✅ компилируется! Бага в типизации
Решение: используй строковый enum или union — там такого нет.
3. Обычный enum тяжелее tree-shake
Webpack/Rollup видят enum как объект с побочными эффектами и могут оставить его в бандле, даже если используется один-два значения.
Решение: const enum или as const объект.
4. const enum нельзя экспортировать в .d.ts для рантайм-потребителей
Если ты пишешь библиотеку и экспортишь const enum — потребитель НЕ сможет использовать его в рантайме (его просто нет).
5. Гетерогенные enum (числа + строки) — anti-pattern
enum Mixed { No = 0, Yes = "YES" } // ❌ не делай так
Сбивает с толку, ломает reverse mapping.
Рекомендация по умолчанию
Нужны типы для конечного набора значений?
├── Используется в рантайме (итерация, ключи)?
│ ├── Да → `as const` объект
│ └── Нет → union type ("a" | "b" | "c")
└── Нужно совместить с библиотечным API, требующим объект?
├── isolatedModules: true (Vite/Next/esbuild)? → обычный enum (строковый)
└── isolatedModules: false (tsc)? → const enum
Связанные темы
- Enum — полное руководство по enum
- as const и Enum —
as constобъект как замена - as const и const assertions — паттерн const assertion
- Литеральные типы — string/number literal types
- Union Types — union как альтернатива enum
- TS — подводные камни — каталог всех gotchas TS