У TypeScript існує спеціальний тип never, який використовується для позначення значень, що ніколи не виникають. На перший погляд це може здатися дивним, але цей тип має цілком практичне призначення – він показує, що функція або вираз не можуть повернути жодного значення, тобто виконання коду ніколи не завершується успішно (наприклад, через помилку або нескінченний цикл).

Коли використовується never

1. Функції, які ніколи нічого не повертають

Тип never застосовується тоді, коли функція не може завершитись нормальним чином – тобто вона або викидає помилку, або виконує нескінченний цикл:

function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {
    // endless loop
  }
}

У цих випадках функція ніколи не повертає значення, тому її результат має тип never.

2. Вичерпні перевірки у switch або if

Тип never також використовується для контролю повноти розгалужень. Це допомагає гарантувати, що ми розглянули всі можливі випадки певного типу:

type Shape = 'circle' | 'square';

function getArea(shape: Shape): number {
  switch (shape) {
    case 'circle':
      return Math.PI * 5 * 5;
    case 'square':
      return 5 * 5;
    default:
      // якщо ми вже розглянули всі варіанти, сюди виконання ніколи не потрапить
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

Якщо всі варіанти типу Shape вже оброблені вище, тоді компілятор вважає, що ми ніколи не потрапимо у default. Тому присвоєння shape до типу never є коректним.

Але якщо додати нове значення до типу:

type Shape = 'circle' | 'square' | 'triangle';

Тоді TypeScript покаже помилку:

Type '"triangle"' is not assignable to type 'never'

Це означає, що наш switch більше не охоплює всі можливі значення типу Shape, і TypeScript таким чином підказує, що ми забули обробити новий варіант 'triangle'.

never vs void

Типи never та void на перший погляд схожі – обидва використовуються у функціях, які «нічого не повертають». Однак між ними є суттєва різниця.

void – функція завершується, але не повертає значення

Тип void означає, що функція успішно завершує виконання, але не повертає жодного значущого результату (фактично – undefined):

function sayHi(): void {
  console.log('Hi!');
}

let speech: void = sayHi();
console.log(speech); // undefined

Навіть якщо ми не вказуємо return, функція з типом повернення void все одно завершується та повертає undefined.

never – функція ніколи не завершується

Тип never навпаки означає, що функція не може завершитись нормальним чином – вона або викидає помилку, або зависає в нескінченному циклі:

function fail(): never {
  throw new Error('Unexpected error!');
}

Ще одна важлива відмінність

Змінна типу void може приймати значення undefined або навіть null (якщо у налаштуваннях компілятора TypeScript вимкнено strictNullChecks), тоді як змінна типу never не може містити жодного значення взагалі:

let something: void = null; // ok
let nothing: never = null; // Error: Type 'null' is not assignable to type 'never'

never та Generics

Тип never часто використовується всередині узагальнених типів (generics), щоб відсікати непотрібні варіанти і робити типи більш точними.

Розглянемо приклад вбудованої утиліти TypeScript – Exclude:

type Exclude<T, U> = T extends U ? never : T;

Якщо тип T відповідає типу U, він замінюється на never, в іншому разі зберігається без змін.

Приклад використання:

type Animals = 'cat' | 'dog' | 'fish';

// Видалимо 'fish' з об’єднання
type NoFish = Exclude<Animals, 'fish'>;
// Результат: 'cat' | 'dog'

Ключова роль never полягає у тому, що цей тип автоматично «зникає» з об’єднання, залишаючи лише релевантні значення.

Навіщо потрібен never

Тип never у TypeScript – це не просто теоретична концепція, а практичний інструмент для створення надійного й передбачуваного коду. Він сигналізує компілятору, що певна частина програми ніколи не має бути досягнута.

Основні переваги використання never:

  • Вичерпна перевірка (Exhaustiveness Checking): never допомагає гарантувати, що всі можливі варіанти в конструкціях switch або if-else оброблені. Якщо ви додасте нове значення до union, TypeScript змусить оновити логіку, що запобігає помилкам під час рефакторингу.
  • Підвищення безпеки типів: Для функцій, які ніколи не повертають значення (наприклад, завжди кидають помилку або працюють у нескінченному циклі), слід вказувати тип повернення never. Це робить код більш передбачуваним.
  • Запобігання неможливим станам: never допомагає створювати типи, які логічно не можуть існувати, захищаючи від некоректного використання API чи компонентів.
  • Покращення роботи з Generics: У складних узагальнених типах never допомагає фільтрувати або обмежувати варіанти, роблячи типи більш точними.

Простіше кажучи, never допомагає уникати неочікуваної поведінки та прихованих помилок. Його використання робить код більш безпечним і зрозумілим.


Мітки: