Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке "discriminated union"?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Discriminated union** - це об'єднання (`union`) об'єктів, у яких є спільна властивість-мітка (дискримінатор), значення якої однозначно визначає тип об'єкта. **Ключове:** завдяки дискримінатору TypeScript автоматично звужує тип (type narrowing) у `if` чи `switch`, без потреби в `as` чи ручних перевірках властивостей.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке discriminated union? **Discriminated union** - це об'єднання (`union`) об'єктів, у яких є **спільна властивість-мітка** (яку називають *discriminant* або *tag*), значення якої однозначно **визначає тип об'єкта**. Це дозволяє TypeScript **розпізнавати**, який саме варіант типу використовується, і **автоматично звужувати тип (type narrowing)** під час перевірок. --- ### Простий приклад ```javascript type Circle = { kind: "circle"; radius: number; }; type Square = { kind: "square"; side: number; }; type Shape = Circle | Square; ``` Тут: - `Shape` - об'єднання (`Circle | Square`); - поле `kind` - **дискримінатор**, воно розрізняє варіанти. --- ### Використання з type narrowing ```javascript function area(shape: Shape): number { if (shape.kind === "circle") { // тут shape: Circle return Math.PI * shape.radius ** 2; } else { // тут shape: Square return shape.side ** 2; } } ``` TypeScript **автоматично визначає**, що в кожному блоці `if/else` тип звузився до потрібного варіанта (`Circle` чи `Square`). --- ## Як це працює TypeScript бачить: 1. об'єднання (`Circle | Square`), 2. спільну властивість (`kind`), 3. і унікальні рядкові літерали в цій властивості. Після перевірки `shape.kind === "circle"`, компілятор знає, що в цьому контексті `shape` **точно Circle**. --- ## Приклад з кількома варіантами ```javascript type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function getArea(shape: Shape) { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; case "triangle": return (shape.base * shape.height) / 2; default: const _exhaustive: never = shape; // перевірка на повноту return _exhaustive; } } ``` Тут: - `switch` автоматично звузить тип за `kind`. - `never` у `default` гарантує, що всі варіанти оброблено. --- ## Переваги discriminated union **1. Безпечне розгалуження** - TS гарантує, що ти обробив усі випадки. **2. Без** `as` **і кастів** - тип визначається автоматично. **3. Чудово працює зі** `switch`**,** `if` **і pattern-подібними перевірками.** **4. Підходить для опису станів, дій, помилок, подій.** --- ## Реальні приклади застосування ### 1. Стани завантаження (state machine pattern) ```javascript type LoadingState = { status: "loading" }; type SuccessState = { status: "success"; data: string }; type ErrorState = { status: "error"; error: Error }; type RequestState = LoadingState | SuccessState | ErrorState; function render(state: RequestState) { switch (state.status) { case "loading": return "Loading..."; case "success": return `Data: ${state.data}`; case "error": return `Error: ${state.error.message}`; } } ``` --- ### 2. Дії у стилі Redux ```javascript type Action = | { type: "add"; payload: number } | { type: "remove"; id: string } | { type: "reset" }; function reducer(state: number[], action: Action) { switch (action.type) { case "add": return [...state, action.payload]; case "remove": return state.filter((_, i) => i.toString() !== action.id); case "reset": return []; } } ``` --- ### 3. Відповіді API ```javascript type ApiResponse<T> = | { kind: "ok"; data: T } | { kind: "error"; message: string }; function handleResponse(res: ApiResponse<string>) { if (res.kind === "ok") { console.log("OK:", res.data); } else { console.error("Error:", res.message); } } ``` --- ## Відмінність від звичайного union | Звичайний union | Discriminated union | |---|---| | `type A = {a: number} \| {b: string}` | `type A = { kind: "x"; a: number } \| { kind: "y"; b: string }` | | Під час перевірки властивостей TypeScript не знає, який це варіант | TypeScript автоматично звужує тип за `kind` | | Потрібно робити перевірки вручну (`'a' in obj`) | Достатньо порівняти мітку (`obj.kind === 'x'`) | --- ## Підсумок | Поняття | Опис | |---|---| | **Discriminant (мітка)** | Спільна властивість з унікальними літеральними значеннями, що визначає варіант типу | | **Discriminated union** | Об'єднання типів, розрізнюваних за міткою | | **Головна перевага** | TypeScript сам звужує тип залежно від значення мітки | | **Типове застосування** | Стани завантаження, actions, варіанти результату, finite state machines |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.