Skip to main content

T extends never

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 nevertrue
never extends anything elsetrue (оскільки never - підтип усіх типів)
something extends neverfalse (крім самого 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 : YCheck<never>X
Неочікуваний розподілT = never дає never, не XIsNeverDistributive<never>never
Правильна перевірка[T] extends [never] ? X : YIsNever<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;

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.