При разработке языка программирования используется понятие bottom типа, который является естественным результатом анализа потока кода. TypeScript выполняет анализ потока кода (😎), поэтому он должен достоверно представлять то, что никогда не произойдёт.
Тип never
используется в TypeScript для обозначения этого типа bottom. Случаи, когда это происходит естественным путем:
- Функция никогда ничего не вернёт (например, если в теле функции есть
while(true){}
) - Функция всегда выбрасывает ошибку (например, в
function foo () {throw new Error ('Не реализована')}
тип возвращаемого значения функцииfoo
-never
)
Конечно, вы можете использовать это описание и сами:
let foo: never; // Okay
Тем не менее, never
может быть присвоено только другое never. Например:
let foo: never = 123; // Ошибка: Тип number нельзя присвоить типу never
// Okay, так как тип возвращаемого значения функции - `never`
let bar: never = (() => { throw new Error('Поднимаю руки вверх, будто мне все равно') })();
Отлично. Теперь давайте перейдем к основному варианту использования :)
Вы можете вызывать функции never в never-обстоятельствах.
function foo(x: string | number): boolean {
if (typeof x === "string") {
return true;
} else if (typeof x === "number") {
return false;
}
// Без типа never мы бы ошиблись:
// - Не все пути кода возвращают значение (строгие проверки на null)
// - Или обнаружен недостижимый код
// Но поскольку TypeScript понимает, что функция `fail` возвращает `never`
// Он может позволить вам вызвать её, поскольку вы могли бы использовать её для безопасности выполнения / тщательных проверок.
return fail("Нетщательный!");
}
function fail(message: string): never { throw new Error(message); }
А поскольку never
назначается только другому never
, вы также можете использовать его для тщательных проверок во время компиляции. Это описано в разделе размеченные объединения.
Как только кто-то говорит вам, что возвращается never
, когда функция никогда не завершается корректно и ничего не будет возвращено, вы интуитивно хотите думать об этом как о void
. Однако void
- это значение. "Never" - ложное утверждение в логике.
Функция, которая ничего не возвращает, возвращает значение void
. Однако функция которая никогда ничего не возвращает (или всегда выбрасывает ошибку), возвращает never
. void
- это то, что может быть присвоено (без strictNullChecking
), но never
никогда не может быть присвоено чему-либо, кроме never
.
Для объявлений функций TypeScript по умолчанию подразумевает void
, как показано ниже:
// Предполагаемый тип возвращаемого значения: void
function failDeclaration(message: string) {
throw new Error(message);
}
// Предполагаемый тип возвращаемого значения: never
const failExpression = function(message: string) {
throw new Error(message);
};
Конечно, вы можете исправить это подробным описанием:
function failDeclaration(message: string): never {
throw new Error(message);
}
Основная причина - обратная совместимость с реальным кодом JavaScript:
class Base {
overrideMe() {
throw new Error("Ты забыл переопределить меня!");
}
}
class Derived extends Base {
overrideMe() {
// Код, который на самом деле возвращается сюда
}
}
Если Base.overrideMe
.
Реальный TypeScript может преодолеть это с помощью
abstract
функций, но этот логический вывод поддерживается для совместимости.