Что такое тип `never` в TypeScript?
never представляет значения, которые никогда не могут существовать — он используется для функций, которые никогда не возвращают управление (бросают исключение или уходят в бесконечный цикл), и как результат невозможных пересечений типов.Тип never в TypeScript
Тип never — это нижний тип (bottom type) в системе типов TypeScript. Он означает «значение, которое никогда не может существовать». В отличие от void, который говорит «функция ничего не возвращает», never говорит «функция никогда не завершится нормально».
Основные случаи использования
1. Функции, которые никогда не возвращают управление
Функция, которая всегда бросает исключение или содержит бесконечный цикл, имеет возвращаемый тип never:
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {
// бесконечный цикл
}
}
2. Исчерпывающая проверка (exhaustiveness check)
Один из самых важных паттернов — гарантировать, что все ветки switch или if-else обработаны. Если компилятор допускает присвоение значения переменной типа never, значит, ветка достижима и что-то не так:
type Shape = 'circle' | 'square' | 'triangle';
function getArea(shape: Shape): number {
switch (shape) {
case 'circle':
return Math.PI;
case 'square':
return 1;
case 'triangle':
return 0.5;
default:
// Если сюда дошли — значит добавили новый тип в Shape, но забыли обработать
const exhaustiveCheck: never = shape;
throw new Error(`Неизвестная фигура: ${exhaustiveCheck}`);
}
}
Теперь, если добавить 'pentagon' в тип Shape без обновления switch, TypeScript выдаст ошибку компиляции.
3. Невозможные пересечения типов
Когда пересечение типов не может содержать ни одного значения, TypeScript выводит never:
type Impossible = string & number; // never
type OnlyAdmin = { role: 'admin' } & { role: 'user' }; // never
4. Фильтрация типов через conditional types
never активно используется в утилитарных типах для фильтрации:
// Исключаем из объединения определённые типы
type NonNullable<T> = T extends null | undefined ? never : T;
// Оставляем только строковые ключи
type StringKeys<T> = {
[K in keyof T]: T[K] extends string ? K : never;
}[keyof T];
type User = { name: string; age: number; email: string };
type StringUserKeys = StringKeys<User>; // 'name' | 'email'
never vs void vs unknown
| Тип | Смысл |
|---|---|
void | Функция завершилась, но без значения |
never | Функция никогда не завершится |
unknown | Значение есть, но тип неизвестен |
Поведение в объединениях и пересечениях
never ведёт себя как нейтральный элемент:
- В объединении
neverисчезает:string | never→string - В пересечении
neverпоглощает всё:string & never→never
Это свойство делает never фундаментальным строительным блоком для создания сложных conditional types.
Что хочет услышать интервьюер
Понимание, что `never` — нижний тип, значения которого не могут существовать во время выполнения
Знание двух ключевых сценариев: функции без возврата (throw/infinite loop) и исчерпывающие проверки в switch
Умение отличить `never` от `void` — особенно важно для middle-разработчика
Практическое применение в conditional types и дженериках для фильтрации типов
Понимание поведения `never` в объединениях (исчезает) и пересечениях (поглощает)
Пример: Основные сценарии использования never
// Функция, которая никогда не возвращает управление
function fail(message: string): never {
throw new Error(message);
}
// Exhaustiveness check — защита от забытых кейсов
type Status = 'active' | 'inactive' | 'banned';
function handleStatus(status: Status): string {
switch (status) {
case 'active':
return 'Пользователь активен';
case 'inactive':
return 'Пользователь неактивен';
case 'banned':
return 'Пользователь заблокирован';
default:
// Если добавить новый статус в тип Status —
// TypeScript выдаст ошибку именно здесь
const _exhaustive: never = status;
return fail(`Необработанный статус: ${_exhaustive}`);
}
}
Пример: never в дженериках и conditional types
// never в conditional types — фильтрация ключей объекта
type PickByValue<T, V> = {
[K in keyof T]: T[K] extends V ? K : never;
}[keyof T];
interface Product {
id: number;
name: string;
price: number;
description: string;
}
// Получаем только строковые поля
type StringFields = PickByValue<Product, string>;
// Результат: 'name' | 'description'
// never исчезает в объединениях
type Result = string | never; // string
// never поглощает в пересечениях
type Impossible = string & never; // never
Типичные ошибки
Путают `never` и `void` — считают, что обе означают «нет возвращаемого значения»
Не знают про exhaustiveness check — самое практически ценное применение `never`
Думают, что `never` можно присвоить переменной с реальным значением
Не понимают, почему `never` исчезает в объединениях типов
Считают `never` экзотикой без практической пользы, не зная про conditional types


