Может кто-нибудь объяснить мне, почему приведен следующий код:
let f = () => {
throw new Error("Should never get here");
}
let g = function() {
throw new Error("Should never get here");
}
function h() {
throw new Error("Should never get here");
}
Выводятся следующие типы:
-
f
() => never
-
g
() => never
-
h
() => void
Я бы ожидал, что тип h
будет () => never
.
Спасибо!
Ответ 1
Отличный вопрос. Разница в том, что f
и g
являются функциональными выражениями, где h
является объявлением функции. Когда функция throw
- только, она получает тип never
, если это выражение, и void
, если это объявление.
Несомненно, этот абзац на самом деле не помогает. Почему существует разница в поведении между выражением функции и объявлением функции? Давайте посмотрим на некоторые встречные примеры в каждом случае.
Плохая идея # 1: Сделать выражения функции throw return void
Рассмотрим некоторый код:
function iif(value: boolean, whenTrue: () => number, whenFalse: () => number): number {
return value ? whenTrue() : whenFalse();
}
let x = iif(2 > 3,
() => { throw new Error("haven't implemented backwards-day logic yet"); },
() => 14);
Этот код в порядке? Должен быть! Обычно писать функцию throw
ing, когда мы считаем, что функция не должна вызываться или ее нужно вызывать только в случаях ошибок. Если тип выражения функции был void
, вызов iif
был бы отклонен.
Итак, из этого примера видно, что выражения функций, которые только throw
должны возвращать never
, а не void
. И действительно, это должно быть нашим предположением по умолчанию, потому что эти функции соответствуют определению never
(в правильно напечатанной программе значение типа never
не может быть соблюдено).
Плохая идея # 2: Сделать декларации функции throwing never
Прочитав предыдущий раздел, вы должны сказать "Отлично, почему не все функции бросания возвращаются never
, то?"
Короткий ответ заключается в том, что это стало большим нарушением. Там много кода (особенно код, предшествующий ключевому слову abstract
), который выглядит так:
class Base {
overrideMe() {
throw new Error("You forgot to override me!");
}
}
class Derived extends Base {
overrideMe() {
// Code that actually returns here
}
}
Но функция, возвращающая void
, не может быть заменена функцией, возвращающей never
(помните, что в правильно напечатанной программе значения never
не могут быть соблюдены), поэтому сделать Base#overrideMe
return never
предотвращает Derived
от реализации реализации этого метода не never
.
И вообще, в то время как выражения функций, которые всегда бросаются, часто существуют как разновидности заполнителей для Debug.fail
, объявления функций, которые всегда бросаются, очень редки. Выражения часто получают псевдоним или игнорируются, тогда как объявления статичны. Объявление функции, что throw
сегодня, скорее всего, сделает что-то полезное завтра; в отсутствие аннотации типа возврата более безопасная вещь void
(т.е. еще не смотри этот тип возврата), а не never
(т.е. эта функция является черной дырой, которая будет потреблять текущий стек выполнения).