TypeScript 从不进行类型推断

TypeScript 从不进行类型推断

问题描述:

谁能解释一下为什么给出以下代码:

Can someone please explain me why given the following code:

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() =>从不
  • g() =>从不
  • h() =>无效
  • f is () => never
  • g is () => never
  • h is () => void

我希望 h 的类型是 () =>也从不.

I would expect the type of h to be () => never as well.

谢谢!

很好的问题.区别在于fg是函数表达式,其中h是函数声明em>.当一个函数是 throw-only 时,如果它是一个表达式,它会得到 never 类型,如果它是一个声明,它会得到 void 类型.

Great question. The difference is that f and g are function expressions, where h is a function declaration. When a function is throw-only, it gets the type never if it's an expression, and void if it's a declaration.

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

Surely the above paragraph doesn't actually help. Why is there a difference in behavior between a function expression and a function declaration? Let's look at some counter-examples in each case.

考虑一些代码:

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);

这个代码好吗?它应该是!当我们认为不应调用该函数或仅应在错误情况下调用该函数时,通常会编写 throwing 函数.但是,如果函数表达式的类型是 void,则对 iif 的调用将被拒绝.

Is this code OK? It should be! It's common to write a throwing function when we believe the function shouldn't be called, or should only be called in error cases. If the type of the function expression were void, though, the call to iif would be rejected.

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

So it's clear from this example that function expressions which only throw ought to return never, not void. And really this should be our default assumption, because these functions fit the definition of never (in a correctly-typed program, a value of type never cannot be observed).

阅读上一节后,您应该说太好了,为什么不是所有的抛出函数都返回 never 呢?"

After reading the prior section, you should be saying "Great, why don't all throwing functions return never, then?"

简而言之,事实证明这样做是一个重大的突破性变化.有很多代码(尤其是在 abstract 关键字之前的代码)看起来像这样

The short answer is it turned out to be a big breaking change to do so. There's a lot of code out there (especially code predating the abstract keyword) that looks like this

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 实现那个方法.

But a function returning void can't be substituted for a function returning never (remember, in a correctly-typed program, never values cannot be observed), so making Base#overrideMe return never prevents Derived from providing any non-never implementation of that method.

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

And generally, while function expressions that always throw often exist as sort of placeholders for Debug.fail, function declarations that always throw are very rare. Expressions frequently get aliased or ignored, whereas declarations are static. A function declaration that throws today is actually likely to do something useful tomorrow; in the absence of a return type annotation, the safer thing to provide is void (i.e. don't look at this return type yet) rather than never (i.e. this function is a black hole that will eat the current execution stack).