Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «T extends never». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Тип `never` означає "значення, яке ніколи не може існувати" - він є підтипом усіх типів, але жоден тип, крім самого `never`, не є підтипом `never`. **Ключове:** умовний тип `T extends never ? X : Y` розподіляється по union-типах, тому для коректної перевірки, чи є `T` саме `never`, тип потрібно загорнути в кортеж: `[T] extends [never] ? X : Y`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## 1. Що таке `never` Тип `never` означає: > "значення, яке **ніколи не може існувати**". Приклади: ```javascript function fail(): never { throw new Error("Error!"); } ``` `never` - підтип **усіх типів**, але **ніхто не є підтипом** `never`, крім самого `never`. --- ## 2. Що робить `T extends never ? X : Y` Це **умовний тип** (conditional type), який можна прочитати як: > "Якщо тип `T` - це `never`, повернути `X`, інакше - `Y`." Приклад: ```javascript type Check<T> = T extends never ? "YES" : "NO"; type A = Check<never>; // "YES" type B = Check<string>; // "NO" type C = Check<any>; // "NO" ``` `A` поверне `"YES"`, тому що `T` дійсно `never`. --- ## 3. Але! Особливість: умовні типи **розподіляються** по union-типах Якщо `T` - об'єднання (`A | B | C`), TypeScript **застосовує умову окремо до кожного члена**. ```javascript type Example<T> = T extends never ? "Y" : "N"; type R1 = Example<string | never | number>; // Крок за кроком: // string → "N" // never → "Y" // number → "N" // R1 = "N" | "Y" | "N" → "N" | "Y" ``` Навіть якщо всередині union є `never`, він **"розчиняється"** (не впливає), і підсумок залишається `"N"` | `"Y"`. --- ## 4. Чому `never` іноді "зникає" в union'ах `never` - "поглинальний" елемент об'єднань: ```javascript string | never // → string number | never // → number ``` Тому при підстановці в `T extends ...` - якщо `T` **взагалі не існує** (наприклад, після фільтрації), результат не розподіляється і може перетворитися на `never`. --- ## Приклад 1: перевірка "порожнього" типу ```javascript type IsNever<T> = [T] extends [never] ? true : false; type A = IsNever<never>; // true type B = IsNever<string>; // false type C = IsNever<any>; // false ``` > Чому квадратні дужки? > Вони **вимикають дистрибутивність**, і перевірка виконується для типу цілком, > а не для кожного члена union. --- ## Приклад 2: без дужок - результат неочікуваний ```javascript type IsNeverDistributive<T> = T extends never ? true : false; type A = IsNeverDistributive<never>; // never (!) - а не true ``` Тому що умовний тип "розподілився" по порожній множині (`never` → немає членів → немає результату). Тому результат став `never`, а не `true`. Виправлення: ```javascript type IsNever<T> = [T] extends [never] ? true : false; ``` --- ## Приклад 3: використання у фільтрації типів ```javascript type FilterNever<T> = T extends never ? never : T; type A = FilterNever<string | never | number>; // string | number ``` > "Відфільтрували" всі `never` з union-типу. --- ## Приклад 4: перевірка в умовних утилітах ```javascript type SafeReturn<T> = T extends never ? "No value" : T; type A = SafeReturn<never>; // "No value" type B = SafeReturn<"ok">; // "ok" ``` --- ## Коротко про механіку | Ситуація | Що відбувається | | --- | --- | | `never extends never` | true | | `never extends anything else` | true (оскільки `never` - підтип усіх типів) | | `something extends never` | false (крім самого `never`) | | `T extends never ? X : Y` | Поверне `X`, тільки якщо `T === never` | | `T extends never ? X : Y` (з union) | Розподіляється по кожному члену union | | `[T] extends [never] ? X : Y` | Перевіряє *весь тип* без розподілу | --- ## Підсумок | Перевірка | Поведінка | Приклад | | --- | --- | --- | | **Проста перевірка** | `T extends never ? X : Y` | `Check<never>` → `X` | | **Неочікуваний розподіл** | `T` = `never` дає `never`, не `X` | `IsNeverDistributive<never>` → `never` | | **Правильна перевірка** | `[T] extends [never] ? X : Y` | `IsNever<never>` → `true` | | **Фільтрація** | `T extends never ? never : T` | Прибирає `never` з union | | **Причина** | `never` - "порожня множина типів" | Не містить значень, але підтип будь-якого типу | --- ### Простіше кажучи - `T extends never ? X : Y` - умовний тип для перевірки "порожнечі". - Але при `T = never` він сам перетворюється на `never`. - Щоб коректно перевірити "чи є тип `never`", потрібно **загорнути тип у кортеж**: `type IsNever<T> = [T] extends [never] ? true : false;`Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.