Задача та сама - обмежити значення фіксованим набором. Підходи різні.
enum - конструкція TypeScript, яка генерує JavaScript-код (об'єкт під час виконання):
enum Status {
Draft = 'draft',
Published = 'published',
}
function publish(status: Status) {}
publish(Status.Published);
publish('published'); // помилка: рядок не є Status
Об'єднання літеральних типів - лише тип, після компіляції зникає повністю:
type Status = 'draft' | 'published';
function publish(status: Status) {}
publish('published'); // ок
Аргументи на користь об'єднання:
- нуль коду під час виконання - тип стирається;
- сумісність зі звичайними рядками: значення з API (
'published') одразу підходять, без перетворень; - сумісність з інструментами, що лише стирають типи: Node.js із вбудованим запуском TypeScript,
erasableSyntaxOnly, швидкі транспілятори.enum- не «стиральний» синтаксис: його треба перетворювати на код, і зerasableSyntaxOnlyкомпілятор його забороняє; - числові
enumмають історичні дивацтва: зворотне відображення (Status[0] === 'Draft'), а у старих версіях TypeScript дозволяв присвоїти будь-яке число. З TS 5.0 присвоєння числа поза значеннями enum - помилка.
Коли потрібен ще й список значень (для <select>, валідації) - об'єкт з as const:
const STATUSES = ['draft', 'published', 'archived'] as const;
type Status = (typeof STATUSES)[number]; // 'draft' | 'published' | 'archived'
STATUSES.includes(value); // перевірка під час виконання
Або об'єкт-«перелік»:
const Status = { Draft: 'draft', Published: 'published' } as const;
type Status = (typeof Status)[keyof typeof Status];
Один і той самий ідентифікатор - і значення, і тип: використання як в enum, але без генерації коду.
const enum вбудовує значення під час компіляції, але не працює з ізольованою транспіляцією (кожен файл окремо - Vite, esbuild) і має проблеми з бібліотеками.
Практична рекомендація: для нового коду - об'єднання літералів або as const-об'єкти. enum - якщо так прийнято в проєкті чи потрібна сумісність з наявним кодом.
Зв'язок з Laravel: PHP-енуми (enum Status: string) зручно генерувати у TypeScript як об'єднання літералів - значення збігаються з тими, що приходять у JSON.