as const и Enum

as const и enum — два подхода к созданию именованных констант в TypeScript: enum создаёт рантайм-объект и тип одновременно, as const фиксирует литеральные типы обычного объекта без генерации дополнительного кода.

Зачем нужно

Перечисления нужны для ограниченного набора допустимых значений (статусы, роли, направления). enum — встроенный механизм, но он создаёт рантайм-объект и имеет неочевидное поведение. as const с объектом — современная альтернатива без сюрпризов.

Где используется

  • Статусы сущностей: PENDING | ACTIVE | INACTIVE
  • Роли пользователей: ADMIN | USER | GUEST
  • Направления, HTTP-методы, коды ошибок
  • Константы конфигурации

Основной контент

Numeric enum

enum Direction {
  Up,    // 0
  Down,  // 1
  Left,  // 2
  Right, // 3
}

function move(dir: Direction): void {
  console.log(dir); // 0, 1, 2 или 3
}

move(Direction.Up); // OK
// move(0);         // OK! TypeScript принимает число — неожиданно

// Reverse mapping: Direction[0] === "Up"
console.log(Direction[Direction.Up]); // "Up"

String enum

enum Status {
  Pending  = "PENDING",
  Active   = "ACTIVE",
  Inactive = "INACTIVE",
}

function updateStatus(s: Status): void { /* ... */ }

updateStatus(Status.Active); // OK
// updateStatus("ACTIVE");   // Error — нужен именно Status.Active

as const объект — современная альтернатива

const Direction = {
  Up:    "UP",
  Down:  "DOWN",
  Left:  "LEFT",
  Right: "RIGHT",
} as const;

// Тип значений:
type Direction = typeof Direction[keyof typeof Direction];
// "UP" | "DOWN" | "LEFT" | "RIGHT"

function move(dir: Direction): void {
  console.log(dir);
}

move(Direction.Up);  // OK
move("UP");          // OK — строка совместима с literal type
move("diagonal");    // Error

Сравнение: enum vs as const

// Enum — рантайм объект, компилируется в JS
enum Color { Red = "RED", Green = "GREEN", Blue = "BLUE" }
// Компилируется в:
// var Color;
// Color["Red"] = "RED"; Color["Green"] = "GREEN"; ...

// as const — только тип, никакого рантайм-кода кроме объекта
const Color = { Red: "RED", Green: "GREEN", Blue: "BLUE" } as const;
// Компилируется в:
// const Color = { Red: "RED", Green: "GREEN", Blue: "BLUE" };

const enum — inlined значения

const enum HttpMethod {
  Get    = "GET",
  Post   = "POST",
  Put    = "PUT",
  Delete = "DELETE",
}

// TypeScript inlines значения при компиляции:
const method = HttpMethod.Get;
// → const method = "GET"; (объект не создаётся)

// Ограничение: const enum нельзя использовать в другом пакете
// (при isolatedModules: true)

Когда что выбирать

// Выбирайте as const если:
// - Нужна совместимость со строковыми литералами
// - Хотите минимальный рантайм код
// - Работаете с bundler и isolatedModules
const ROLES = { Admin: "admin", User: "user", Guest: "guest" } as const;
type Role = typeof ROLES[keyof typeof ROLES]; // "admin" | "user" | "guest"

// Выбирайте enum если:
// - Нужен reverse mapping (Direction[0] === "Up")
// - Команда привыкла к enum из C#/Java
// - Нужна строгая изоляция (string enum)

Частые ошибки

  • Numeric enum принимает произвольные числаmove(42) не вызовет ошибку при numeric enum; string enum строже.
  • Ambient enum в isolatedModulesconst enum несовместим с isolatedModules: true; используйте regular enum или as const.
  • Забыть as const — без него TypeScript расширяет тип полей до string, а не "admin" | "user".
  • Смешивать enum и string — значение Status.Active и "ACTIVE" несовместимы при string enum; нужно явное сравнение.

Связанные темы

Ресурсы