Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке дистрибутивна поведінка union-типів?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Дистрибутивність (distributive behavior)** означає, що коли TypeScript застосовує умовний тип (`extends ? :`) до union-типу з голим параметром-типом, він автоматично розкладає вираз по кожному елементу об'єднання, а потім знову об'єднує результати в `union`. **Ключове:** дистрибутивність можна вимкнути, обгорнувши тип у кортеж - `[T] extends [U] ? ... : ...`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке **дистрибутивна поведінка union-типів** **Дистрибутивність (distributive behavior)** означає, що коли TypeScript застосовує **умовний тип (**`extends ? :`**)** до **union-типу**, він **автоматично розкладає** вираз **по кожному елементу об'єднання** і потім **об'єднує результати назад** у `union`. --- ## Приклад 1. Проста ілюстрація ```javascript type T = string | number; type Box<T> = T extends any ? { value: T } : never; ``` Що станеться: - TypeScript візьме `string` - потім `number` - застосує умову до кожного - і об'єднає результати назад ```javascript type Result = Box<string | number>; // Розкладається як: // Box<string> | Box<number> // -> { value: string } | { value: number } ``` Підсумок: `Box<string | number>` ≡ `{ value: string } | { value: number }`. --- ## Чому так відбувається Тому що **в умовних типах**, коли зліва від `extends` стоїть **параметр-тип** (`T`), а не конкретний тип на кшталт `{}` чи `unknown`, TypeScript **розкриває об'єднання** і застосовує умову **окремо до кожного елемента**. > Це особливе правило - "distributive conditional types". --- ## Приклад 2. Перевіримо недистрибутивну поведінку Якщо обгорнути `T` у "контейнер", дистрибутивність **зникає**: ```javascript type Boxed<T> = [T] extends [any] ? { value: T } : never; type Result = Boxed<string | number>; // Тепер не розподіляється! // [string | number] не розширює [any] дистрибутивно // Результат: { value: string | number } ``` Різниця: | Вираз | Результат | |---|---| | `T extends any ? X<T> : never` | `X<string> \| X<number>` | | `[T] extends [any] ? X<T> : never` | `X<string \| number>` | Обгортка `[]` вимикає дистрибутивність. --- ## Приклад 3. Реальний кейс - вилучення підтипу ```javascript type ExtractString<T> = T extends string ? T : never; type R = ExtractString<string | number | boolean>; // розподіляється: // string -> string // number -> never // boolean -> never // об'єднуємо: string | never | never -> string ``` Підсумок: `ExtractString<string | number | boolean>` -> `string`. > Так працюють вбудовані утиліти TypeScript на кшталт `Extract<>`, `Exclude<>`, `NonNullable<>` та ін. --- ## Приклад 4. Використання в Exclude і Extract ```javascript type Exclude<T, U> = T extends U ? never : T; type R = Exclude<"a" | "b" | "c", "a" | "c">; // розподіляється: // "a" extends "a"|"c" -> never // "b" extends "a"|"c" -> "b" // "c" extends "a"|"c" -> never // => "b" ``` > Ось чому `Exclude<"a" | "b" | "c", "a" | "c">` дає `"b"`. --- ## Приклад 5. Порівняння з "недистрибутивною" версією ```javascript type NonDistributiveExclude<T, U> = [T] extends [U] ? never : T; type R = NonDistributiveExclude<"a" | "b" | "c", "a" | "c">; // [T] не розкладається // => ["a" | "b" | "c"] extends ["a" | "c"] -> false // => результат = "a" | "b" | "c" ``` Обгортаючи `T` у кортеж `[]`, ми **вимикаємо дистрибутивність** і змушуємо TypeScript розглядати об'єднання як одне ціле. --- ## Приклад 6. Ще більш наочна візуалізація Уяви union як множину: ```javascript T = {A, B, C} T extends X ? Y : Z ``` TypeScript робить: ```javascript (A extends X ? Y : Z) | (B extends X ? Y : Z) | (C extends X ? Y : Z) ``` --- ## Ключові висновки | Особливість | Поведінка | |---|---| | Працює лише в умовних типах (`extends ? :`) | Так | | Активується, якщо `T` - naked type parameter (голий параметр) | Так | | Можна вимкнути, обгорнувши `T` у `[]` | Так | | Основа утиліт `Exclude`, `Extract`, `NonNullable` | Так | | Застосовується поелементно до кожного члена union | Так | --- ## Простий підсумок > **Дистрибутивна поведінка union-типів** - > це коли TypeScript **застосовує умовний тип до кожного елемента об'єднання окремо**, > а потім **об'єднує результати назад у union**. > > Можна **вимкнути** це, обгорнувши тип у кортеж: `[T] extends [U] ? ... : ...`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.