← Назад к вопросам

В чем разница между Literal Types и Enum в TypeScript?

1.8 Middle🔥 172 комментариев
#JavaScript Core#TypeScript

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Literal Types и Enum в TypeScript

Хотя оба используются для ограничения возможных значений переменной, они имеют существенные различия в реализации, производительности и поведении.

1. Literal Types

Определение: Конкретное значение переменной является её типом. Это ограничивает переменную принимать только одно точное значение.

// Простые literal types
type Status = 'pending' | 'success' | 'error';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Direction = 'up' | 'down' | 'left' | 'right';

// Использование
let currentStatus: Status = 'pending';
currentStatus = 'success';  // OK
currentStatus = 'loading';  // Error: Type '"loading"' is not assignable

// С числами
type StatusCode = 200 | 201 | 400 | 404 | 500;
let code: StatusCode = 200;  // OK
code = 201;                  // OK
code = 202;                  // Error

Характеристики:

  • Typescript-only конструкция (стирается при компиляции)
  • Структурная типизация
  • Никак не влияет на финальный JavaScript
  • Нет runtime представления
// Скомпилированный JavaScript (TypeScript убирает типы)
let currentStatus = 'pending';  // просто переменная со строкой

2. Enum

Определение: Это специальная конструкция TypeScript, которая создает именованный набор констант. В отличие от Literal Types, Enum создает реальный объект в JavaScript.

// String Enum
enum Status {
  Pending = 'pending',
  Success = 'success',
  Error = 'error'
}

// Использование
let currentStatus: Status = Status.Pending;
console.log(currentStatus);  // 'pending'

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

let dir: Direction = Direction.Up;
console.log(dir);  // 0

// Enum без явных значений (по умолчанию число от 0)
enum Color {
  Red,      // 0
  Green,    // 1
  Blue      // 2
}

console.log(Color.Red);    // 0
console.log(Color.Blue);   // 2

Скомпилированный JavaScript (Enum создает реальный объект!):

// String Enum компилируется в объект
var Status;
(function (Status) {
    Status["Pending"] = "pending";
    Status["Success"] = "success";
    Status["Error"] = "error";
})(Status || (Status = {}));

// Numeric Enum
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 = {}));

Ключевые различия

1. Существование в Runtime

// Literal Types - стирается полностью
type Status = 'pending' | 'success' | 'error';

console.log(typeof Status);  // Error: 'Status' only refers to a type

// Enum - существует как реальный объект
enum Status {
  Pending = 'pending',
  Success = 'success'
}

console.log(typeof Status);  // 'object'
console.log(Status);         // { Pending: 'pending', Success: 'success' }
console.log(Status.Pending); // 'pending'

2. Размер Bundle

// Literal Types
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

function request(method: HttpMethod) {
  // компилируется в обычную функцию без типов
}

// Скомпилированный JS - нет доп. кода
function request(method) {
  // тело функции
}

// Enum - добавляет код в бандл
enum HttpMethod {
  Get = 'GET',
  Post = 'POST',
  Put = 'PUT',
  Delete = 'DELETE'
}

// Скомпилированный JS - много доп. кода для Enum
var HttpMethod;
(function (HttpMethod) {
    HttpMethod["Get"] = "GET";
    HttpMethod["Post"] = "POST";
    HttpMethod["Put"] = "PUT";
    HttpMethod["Delete"] = "DELETE";
})(HttpMethod || (HttpMethod = {}));

3. Обратная совместимость (reverse mapping)

// Numeric Enum - есть reverse mapping
enum Status {
  Pending = 0,
  Success = 1
}

console.log(Status[0]);       // 'Pending' - можно получить название по числу!
console.log(Status.Pending);  // 0

// String Enum - нет reverse mapping
enum Color {
  Red = 'red',
  Blue = 'blue'
}

console.log(Color['red']);   // undefined
console.log(Color.Red);      // 'red'

// Literal Types - обратная совместимость не нужна (это просто тип)

4. Использование как тип vs Использование как значение

// Literal Types - ТОЛЬКО тип
type Direction = 'up' | 'down';
const move = (dir: Direction) => {};
move('up');  // OK

// Enum - и тип И значение
enum Direction {
  Up = 'up',
  Down = 'down'
}

// Как тип
const move = (dir: Direction) => {};

// Как значение - можно передать сам Enum
move(Direction.Up);     // OK
const myDir = Direction.Down;

// Можно проверить значения
const directions = [Direction.Up, Direction.Down];

5. Расширяемость

// Literal Types - строгие, нельзя расширить
type Status = 'pending' | 'success';
// Нельзя добавить еще значение позже

// Но можно создать новый тип объединением
type ExtendedStatus = Status | 'error';

// Enum - можно объявить несколько раз (ambient declaration)
enum Status {
  Pending = 'pending'
}

// Позже в другом месте
enum Status {
  Success = 'success'
}

// Результат: Status имеет обе значения
console.log(Status.Pending);   // 'pending'
console.log(Status.Success);   // 'success'

6. JSON сериализация

// Literal Types - сериализуются как обычные значения
type Status = 'pending' | 'success';

const user = {
  name: 'John',
  status: 'pending' as Status
};

JSON.stringify(user);
// {"name":"John","status":"pending"}

// Enum - может быть проблема с numeric enum
enum StatusCode {
  OK = 200,
  NotFound = 404
}

const response = {
  code: StatusCode.OK
};

JSON.stringify(response);
// {"code":200}  - это число, не текст!

// String Enum работает как обычно
enum Status {
  Pending = 'pending',
  Success = 'success'
}

const user = {
  status: Status.Pending
};

JSON.stringify(user);
// {"status":"pending"}

Таблица сравнения

СвойствоLiteral TypesEnum
Существует в runtimeНетДа
Размер бандла0 доп. кода+Размер объекта
Может быть значениемНет (только тип)Да (и тип и значение)
Reverse mapping-Да (для numeric)
Легко проверить значениеНет extra кодаЕсть Enum объект
Стирается при компиляцииДаНет
Можно расширить позжеС новым UnionДа, через redeclaration
Для JSONПрямое значениеМожет быть число

Практические примеры выбора

// ИСПОЛЬЗУЙ LITERAL TYPES для:
// 1. Простых наборов значений
type ButtonVariant = 'primary' | 'secondary' | 'outline';

// 2. Когда значение не нужно в runtime
interface ButtonProps {
  variant: 'primary' | 'secondary';
}

// 3. Когда нужно минимизировать бандл
type Environment = 'development' | 'staging' | 'production';

// 4. Объединение с другими типами
type FormStatus = 'idle' | 'loading' | 'success' | 'error';
type UserState = FormStatus | 'offline';
// ИСПОЛЬЗУЙ ENUM для:
// 1. Когда нужны именованные константы в коде
enum ApiStatus {
  Ok = 200,
  BadRequest = 400,
  NotFound = 404
}

if (response.status === ApiStatus.Ok) { }  // Понятнее чем === 200

// 2. Когда нужно итерировать по значениям
enum Colors {
  Red = 'red',
  Green = 'green',
  Blue = 'blue'
}

// Можно получить все значения
Object.values(Colors);  // ['red', 'green', 'blue']

// 3. При работе с старым кодом, где нужны runtime объекты
// 4. Когда важна обратная совместимость значений

Лучшие практики

Рекомендация современных подходов:

// ИЗБЕГАЙ numeric enum
enum Status {
  Pending = 0,    // Опасно: легко спутать с false
  Success = 1
}

// ЛУЧШЕ
type Status = 'pending' | 'success' | 'error';

// ИЛИ если нужны константы
const Status = {
  Pending: 'pending',
  Success: 'success',
  Error: 'error'
} as const;

type Status = typeof Status[keyof typeof Status];

// ИЛИ string enum если действительно нужен Enum
enum Status {
  Pending = 'pending',
  Success = 'success',
  Error = 'error'
}

Заключение

  • Literal Types предпочтительны в большинстве современных приложений (легче, меньше кода)
  • Enum полезны когда нужны именованные константы и runtime доступ
  • Для новых проектов рекомендуется использовать Literal Types как основной инструмент
  • String Enum лучше чем Numeric Enum (безопаснее)
  • Рассмотри использование const assertion как альтернативу Enum
В чем разница между Literal Types и Enum в TypeScript? | PrepBro