TypeScript从不输入推断

时间:2016-10-25 23:47:29

标签: typescript types type-inference

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

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 个答案:

答案 0 :(得分:25)

好问题。区别在于fg是函数表达式,其中h是函数声明。当某个函数仅为throw时,如果它是一个表达式,则会获得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);

这段代码好吗?它应该是!当我们认为不应该调用函数时,或者只应在错误情况下调用函数时,编写throw函数是很常见的。但是,如果函数表达式的类型为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
    }
}

但是返回void的函数无法替换返回never的函数(请记住,在正确类型的程序中,无法观察到never个值),所以使Base#overrideMe返回never会阻止Derived提供该方法的任何非never实现。

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