Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке enum у TypeScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Enum** у TypeScript - це набір іменованих констант, який компілюється в JavaScript-об'єкт. ```typescript enum Status { Pending, InProgress, Done } // числовий: 0, 1, 2 enum Role { Admin = 'admin', User = 'user' } // рядковий: явні значення const s: Status = Status.Done; // 2 console.log(Status[2]); // 'Done' (зворотнє відображення, тільки числовий) ``` **Головне:** числові enum підтримують зворотнє відображення, рядкові - ні. `const enum` підставляє значення в код без об'єкта в рантаймі.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Enum** у TypeScript - це набір іменованих констант, який компілюється в JavaScript-об'єкт. Замість "магічних" рядків і чисел розкиданих по коду ти отримуєш безпечні типи і зрозумілі імена. ## Теорія ### TL;DR - Enum схожий на список контактів: телефонуєш по імені, а не по номеру. TypeScript стежить, щоб використовувались тільки дійсні імена. - **Числові enum** автоматично інкрементуються (0, 1, 2...) і підтримують зворотнє відображення. **Рядкові enum** вимагають явних значень і зворотнього відображення не мають. - `const enum` підставляє значення в код під час компіляції. Жодного об'єкта в рантаймі. - Для фіксованих наборів (статуси, ролі, напрямки) - enum. Для даних ззовні - union types. ### Короткий приклад ```typescript enum Status { Pending = 0, InProgress = 1, Done = 2 } const task: Status = Status.Done; // типобезпечно console.log(task); // 2 console.log(Status[2]); // 'Done' (зворотнє відображення) // Рядковий enum enum Role { Admin = 'admin', User = 'user' } const userRole: Role = Role.Admin; console.log(userRole); // 'admin' (зворотнього відображення немає) ``` Числові enum підтримують зворотнє відображення - `Status[2]` повертає `'Done'`. Рядкові не підтримують. Саме ця різниця визначає більшість компромісів нижче. ### Головна різниця Числовий enum компілюється в об'єкт з двосторонніми записами: ім'я -> значення і значення -> ім'я. Тому `Status[2]` повертає `'Done'`. Рядковий enum зберігає тільки пряме відображення, що робить його зручнішим для JSON і API. Ціна питання - немає зворотнього пошуку. ### Коли що використовувати - **Числовий enum:** стани гри, HTTP-статус-коди, ідентифікатори в БД де може знадобитись зворотнє відображення - **Рядковий enum:** ролі користувачів, рівні дозволів, типи подій що серіалізуються в JSON або передаються через API - **const enum:** якщо важлива продуктивність і потрібні вбудовані значення без об'єкта в рантаймі - **Union types замість enum:** коли значення приходять ззовні або потрібне легше рішення лише для компайл-тайму ### Як TypeScript компілює enum TypeScript компілює числовий enum в JavaScript-об'єкт з двома наборами записів: `Status['Pending'] = 0` і `Status[0] = 'Pending'`. Рядкові enum отримують тільки пряме відображення. `const enum` окремий випадок - компілятор підставляє кожне звертання до члена enum як літеральне значення і жодного об'єкта не генерує. Тому `const myColor = Color.Red` стає `const myColor = 0` у вихідному коді. ### Типові помилки **Помилка 1: Object.values() на числовому enum повертає більше ніж числа** ```typescript enum Priority { Low, // 0 Medium, // 1 High // 2 } console.log(Object.values(Priority)); // [0, 1, 2, 'Low', 'Medium', 'High'] - обидва напрямки включені ``` Числовий enum зберігає і `{ 0: 'Low', Low: 0, ... }`, тому `Object.values()` повертає шість елементів, а не три. Рішення: фільтрувати по типу або перейти на рядковий enum. **Помилка 2: Object.values() на const enum в рантаймі** ```typescript const enum Status { Active = 'active', Inactive = 'inactive' } const list = Object.values(Status); // Помилка в рантаймі: Status не існує ``` `const enum` не має об'єкта в рантаймі. Якщо потрібна ітерація, використовуй звичайний enum. **Помилка 3: Числові та рядкові значення в одному enum** ```typescript // Погано: зворотнє відображення ламається для числових членів enum Mixed { Success = 1, Error = 'error' } ``` Вибирай один тип. Змішані enum ламають зворотнє відображення і важко читаються. **Помилка 4: Enum для даних з зовнішнього API** ```typescript enum Role { Admin = 'Admin', // велика літера User = 'User' } const fromApi = { role: 'admin' }; // API повертає з маленької літери fromApi.role === Role.Admin; // false - ніколи не збігається ``` Більшість помилок "enum не працює з API" зводяться до такого ж регістру символів. Для даних з API краще підходять union types (`type Role = 'admin' | 'user'`). Або нормалізуй вхідне значення перед порівнянням. ### Де зустрічається в реальних проектах - **React/Redux:** типи екшенів (`enum ActionType { FETCH_START = 'FETCH_START' }`) - **Express/Node.js:** HTTP-статус-коди, типи помилок, стани підключення до БД - **NestJS:** декоратори для контролю доступу на основі ролей використовують enum для рівнів дозволів - **GraphQL:** enum-типи в схемі напряму відображаються на TypeScript enum - **Ігрові рушії:** Phaser і Babylon.js використовують enum для станів гри, клавіш і типів колізій ### Питання на співбесіді **Q:** Яка різниця між enum і union type на кшталт `type Status = 'pending' | 'done'`? **A:** Enum створює об'єкт в рантаймі і дає іменовану групу яку можна використовувати по всій кодовій базі. Union types існують тільки під час компіляції, легші в бандлі і краще підходять для значень що приходять ззовні. Enum обирай коли потрібна ітерація або зворотнє відображення. Union types - для простіших випадків. **Q:** Навіщо `const enum` якщо є звичайний enum? **A:** `const enum` підставляє кожне звертання як літеральне значення під час компіляції. Жодного об'єкта в бандлі. Мінус: `Object.values()`, зворотнє відображення і будь-яка рефлексія в рантаймі стають недоступними. **Q:** Числовий enum має значення 1, 5 і 10. Що поверне `Enum[3]`? **A:** `undefined`. Зворотнє відображення існує тільки для тих числових значень що явно задані в enum. Пропуски в послідовності не мають записів в об'єкті. Реальна пастка якщо використовуєш числові enum для конвертації ідентифікаторів з БД в імена - отримаєш `undefined` без жодної помилки. **Q:** Як безпечно перевірити чи значення з API є членом рядкового enum? **A:** `Object.values(MyEnum).includes(apiValue as MyEnum)`. Це перевіряє в рантаймі що вхідний рядок є одним з дозволених значень перед тим як трактувати його як типізований член enum. ## Приклади ### Базовий: числовий і рядковий enum поруч ```typescript enum HttpStatus { OK = 200, BadRequest = 400, NotFound = 404 } // Зворотнє відображення працює для числових enum const code = 404; console.log(HttpStatus[code]); // 'NotFound' enum OrderStatus { Pending = 'pending', Shipped = 'shipped', Delivered = 'delivered' } // Рядковий enum: зрозумілі значення, чиста серіалізація в JSON const status: OrderStatus = OrderStatus.Shipped; console.log(JSON.stringify({ status })); // {"status":"shipped"} ``` `HttpStatus[404]` повертає `'NotFound'` бо числовий enum зберігає зворотні записи. `OrderStatus` не має зворотнього відображення, але рядкові значення серіалізуються чисто. ### Середній рівень: типобезпечна обробка замовлень в Express ```typescript enum OrderStatus { Pending = 'pending', Processing = 'processing', Shipped = 'shipped', Delivered = 'delivered' } function handleOrder(status: OrderStatus): string { if (status === OrderStatus.Shipped) { return 'Відправляємо сповіщення про доставку'; } // Це не скомпілюється - TypeScript відловлює помилку до рантайму: // if (status === OrderStatus.Cancelled) { } // Error: Property 'Cancelled' does not exist return 'Дія не потрібна'; } handleOrder(OrderStatus.Shipped); // OK handleOrder('shipped'); // Error: Argument of type 'string' is not assignable to type 'OrderStatus' ``` Передача рядка `'shipped'` не пройде навіть якщо значення збігається, бо TypeScript очікує `OrderStatus`, а не `string`. В цьому і є вся суть enum.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.