TypeScript从不输入推断

Rem*_*sen 19 types type-inference typescript

有人可以解释一下为什么给出以下代码:

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");
}
Run Code Online (Sandbox Code Playgroud)

推断出以下类型:

  • f() => never
  • g() => never
  • h() => void

我期望的类型h() => never为好.

谢谢!

Rya*_*ugh 27

好问题.不同的是,fg有功能的表达,在那里h是一个函数声明.当一个函数是throw-only时,never如果它是一个表达式,它将获得类型,void如果它是一个声明.

当然上面的段落实际上没有帮助.为什么函数表达式和函数声明之间的行为存在差异?让我们看看每种情况下的一些反例.

糟糕的主意#1:让投掷函数表达式返回 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);
Run Code Online (Sandbox Code Playgroud)

这段代码好吗?它应该是!throw当我们认为不应该调用函数时,或者只应在错误情况下调用函数时,编写ing函数是很常见的.但是,如果函数表达式的类型是void,则iif拒绝调用.

所以从这个例子中可以清楚地知道,函数表达式只throw应该返回never,而不是void.实际上这应该是我们的默认假设,因为这些函数符合never(在正确类型的程序中,never无法观察到类型的值)的定义.

糟糕的想法#2:让投掷函数声明返回 never

在阅读完前一节之后,你应该说"好了,为什么不把所有的投掷功能都归还never呢?"

简短的回答是,事实证明这是一个重大改变.那里有很多代码(特别是在abstract关键字之前的代码),看起来像这样

class Base {
    overrideMe() {
        throw new Error("You forgot to override me!");
    }
}

class Derived extends Base {
    overrideMe() {
        // Code that actually returns here
    }
}
Run Code Online (Sandbox Code Playgroud)

但是返回的函数void不能替代返回的函数never(记住,在正确类型的程序中,never无法观察到值),因此Base#overrideMe返回会never阻止Derived提供never该方法的任何非实现.

通常,虽然总是抛出的函数表达式经常作为占位符存在,但总是抛出的Debug.fail函数声明非常罕见.表达式经常被别名或忽略,而声明是静态的.函数的声明throw今天的工作实际上是有可能做一些有用的事情明天; 在没有返回类型注释的情况下,提供更安全的东西void(即不要查看此返回类型)而不是never(即此函数是一个黑洞,将占用当前执行堆栈).