Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що означає "discriminated union"?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Discriminated union** - це об'єднання (`union`) кількох типів об'єктів, де **в кожному типі є спільне поле-мітка (дискримінатор)**, яке однозначно вказує, до якого варіанта належить об'єкт. **Ключове:** це union, у всіх членів якого є спільна властивість, і за її значенням TypeScript розуміє, який саме тип перед нами.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке **Discriminated Union** **Discriminated union** - це об'єднання (`union`) кількох типів об'єктів, де **в кожному типі є спільне поле-мітка (дискримінатор)**, яке однозначно вказує, до якого варіанта належить об'єкт. > Простіше кажучи: > це **union**, у всіх членів якого є спільна властивість, > і за її значенням TypeScript **розуміє, який саме тип** перед нами. --- ## Приклад 1. Проста форма дискримінованого об'єднання ```javascript type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number }; function getArea(shape: Shape): number { if (shape.kind === "circle") { // Тут shape: { kind: "circle"; radius: number } return Math.PI * shape.radius ** 2; } else { // Тут shape: { kind: "square"; side: number } return shape.side ** 2; } } ``` - Спільне поле `kind` - **дискримінатор** - TypeScript **автоматично звужує тип** залежно від його значення - Можна безпечно звертатися до `radius` чи `side`, без `as` і без помилок --- ## Чому це зручно У звичайних union-типах TS не знає, який саме об'єкт використовується: ```javascript type Circle = { radius: number }; type Square = { side: number }; type Shape = Circle | Square; function area(shape: Shape) { // shape.radius Помилка: у Square немає radius } ``` Якщо ж додати спільне поле (`kind`), TypeScript може сам здогадатися, хто є хто. --- ## Приклад 2. Кілька варіантів типів ```javascript type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function area(shape: Shape): number { 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; } } ``` Усередині кожного `case` TypeScript **точно знає**, які поля доступні, і підказує їх в автодоповненні. --- ## Приклад 3. Перевірка вичерпності (exhaustiveness check) Дискриміновані union-и дозволяють контролювати, що **всі можливі варіанти оброблено** (важливо під час розширення типів): ```javascript function area(shape: Shape): number { 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: // Помилка, якщо з'явиться новий тип без case const _exhaustiveCheck: never = shape; return _exhaustiveCheck; } } ``` > Отже, якщо ти додаси новий тип (наприклад, `"rectangle"`), > TS одразу покаже, що `switch` більше не покриває всі варіанти. --- ## Приклад 4. Реальний сценарій (API-відповідь) ```javascript type ApiResponse = | { status: "success"; data: string } | { status: "error"; message: string } | { status: "loading" }; function handleResponse(res: ApiResponse) { switch (res.status) { case "success": console.log("Дані:", res.data); break; case "error": console.error("Помилка:", res.message); break; case "loading": console.log("Завантаження..."); break; } } ``` > Один дискримінувальний ключ (`status`) повністю описує стан. > Дуже популярний прийом у React-коді та Redux-логіці. --- ## Правила дискримінованих union-типів | Вимога | Опис | |---|---| | Спільне поле (дискримінатор) | Усі варіанти повинні мати одну й ту саму властивість (наприклад, `kind`, `type`, `status`) | | Унікальні значення | Кожне значення дискримінатора унікальне для свого варіанта | | Тип поля - літеральний | Зазвичай це рядковий літерал (`"circle"`, `"error"`, `"admin"`) | | TS автоматично звужує тип | Перевірка `if` або `switch` виконує narrowing без кастів | --- ## Дискримінований union vs звичайний union | Вид | Приклад | TS може звузити тип автоматично? | |---|---|---| | **Звичайний union** | `{ radius: number } \| { side: number }` | Ні | | **Дискримінований union** | `{ kind: "circle" } \| { kind: "square" }` | Так | --- ## Підсумок | Термін | Значення | |---|---| | **Discriminated union** | Об'єднання типів зі спільним "дискримінувальним" полем | | **Навіщо потрібен** | Щоб TypeScript міг точно визначити тип за значенням поля | | **Як позначається** | Через ` | | **Головна перевага** | Безпечне і зручне звуження типів без ручних перевірок | | **Типове застосування** | Статуси, форми даних, API-відповіді, фігури, стани компонентів | --- ### Коротко: > **Discriminated union** - це об'єднання об'єктів, > у яких є спільне поле-мітка (наприклад, `kind`), > і за значенням цієї мітки TypeScript **автоматично розуміє, який це тип**Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.