Перечисления enum
В лифте бизнес-центра на кнопках написано «Парковка», «Холл» и «Крыша». Человек нажимает кнопку по названию, а лифт получает номер этажа: 0, 1 или 2. Названия удобны людям, номера — машине, а кнопки связывают одно с другим.
В программах такие наборы встречаются постоянно: уровни сложности, статусы заказа, роли пользователей. Можно писать в коде голые числа 0, 1 и 2, но через месяц никто не вспомнит, что значит 2. В TypeScript для именованных наборов значений есть перечисление — его объявляют словом enum.
enum Level {
Low,
Mid,
High,
}
function describe(level: Level) {
console.log(level);
}
describe(Level.Mid); // 1
describe(Level.High); // 2Давайте разберём пример по строкам:
enum Level { … }объявляет перечисление с тремя членами:Low,MidиHigh.- Члены получают номера по порядку, начиная с нуля:
Low—0,Mid—1,High—2. level: Level— имя перечисления работает как тип: параметр принимает членыLevel, аdescribe(7)tsc отклонит: среди значенийLevelнет7. Число, равное значению члена, напримерdescribe(1), tsc пропустит.describe(Level.Mid)печатает1, а неMid: значение члена — число.
Значения членов
Здесь аналогия с лифтом заканчивается. Номера кнопок выбрал завод, а значения членов enum можно задать самому — и числа, и строки.
enum Priority {
Low = 1,
Mid,
High,
}
enum Color {
Red = "RED",
Green = "GREEN",
}
console.log(Priority.Mid, Priority.High); // 2 3
console.log(Color.Green); // GREENСвои числа
Low = 1 задаёт первому члену значение вручную. Член без значения получает значение предыдущего плюс один, поэтому Mid — 2, а High — 3. Это правило работает в любом месте перечисления, а не только в начале.
Строковые значения
Red = "RED" делает перечисление строковым. Такие значения удобно читать в логах и в данных с сервера. Счёт от предыдущего для строк не работает, поэтому значение пишут у каждого члена — иначе tsc сообщит Enum member must have initializer.
enum остаётся в JavaScript
Аннотации и объявления type tsc стирает. С enum так не выходит: его значения нужны при запуске. Вот начало .js-файла, который tsc пишет из первого примера, — Level стал настоящим объектом:
"use strict";
var Level;
(function (Level) {
Level[Level["Low"] = 0] = "Low";
Level[Level["Mid"] = 1] = "Mid";
Level[Level["High"] = 2] = "High";
})(Level || (Level = {}));Каждая строка внутри функции записывает пару в обе стороны: Level.Mid даёт 1, а Level[1] даёт "Mid". Так у числового перечисления можно узнать имя члена по значению.
Этот вопрос часто задают на собеседовании: «Что остаётся от enum после компиляции?» Ответ: объект с членами, а у числового enum — ещё и обратные пары «номер → имя».
Объединение литералов вместо enum
Многие команды вместо enum пишут объединение литеральных типов: type Level = "low" | "mid" | "high";. Оно проверяется так же строго, но целиком исчезает при компиляции и не добавляет в .js ни строчки. enum выбирают, когда нужны имена для чисел или объект со всеми значениями при запуске.
Частая ошибка
Файл с enum запускают через node levels.ts:
enum Level { // ✗
Low,
Mid,
High,
}
console.log(Level.Mid);SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only modetsc этот файл компилирует без ошибок. Отказывает Node: node file.ts работает в режиме, который только стирает типы, а enum нужно превратить в объект. Скомпилируйте файл через tsc levels.ts и запустите node levels.js или проверьте код в TypeScript Playground. Объединение литералов node file.ts запускает без проблем.
Главное
enumдаёт имена набору значений; нумерация идёт с0, а член без значения получает значение предыдущего плюс один.- У строкового
enumзначение пишут у каждого члена. enumне стирается: tsc превращает его в объект в.js, аnode file.tsтакой файл не запускает.- Объединение литералов
"low" | "mid" | "high"— частая заменаenum: оно целиком исчезает при компиляции.