为什么 `Promise.reject().catch(() => 5)` 在 TypeScript 中是有效的

huangapple go评论55阅读模式
英文:

Why Promise.reject().catch(() => 5) valid for Typescript

问题

The TypeScript compiler doesn't complain about this statement because the catch method in TypeScript's Promise interface allows for a wide range of return types. It assumes that the handler function returns TResult | PromiseLike<TResult> where TResult = never, but in your example, the handler function returns 5, which is a number. This might seem unusual, but TypeScript doesn't enforce strict type checking in this case because it's valid for the handler to return a non-Promise value like 5. However, you're correct that 5 is not a PromiseLike because it doesn't have a .then function.

英文:

Could you help me understand why TS compiler doesn't complain about this statement?

Promise.reject().catch(() =&gt; 5)

If checking the definition of the handler function inside the catch we can see the piece of code as follows:

interface Promise&lt;T&gt; {
    then&lt;TResult1 = T, TResult2 = never&gt;(onfulfilled?: ((value: T) =&gt; TResult1 | PromiseLike&lt;TResult1&gt;) | undefined | null, onrejected?: ((reason: any) =&gt; TResult2 | PromiseLike&lt;TResult2&gt;) | undefined | null): Promise&lt;TResult1 | TResult2&gt;;

    catch&lt;TResult = never&gt;(onrejected?: ((reason: any) =&gt; TResult | PromiseLike&lt;TResult&gt;) | undefined | null): Promise&lt;T | TResult&gt;;
}

It assumes that the handler function returns TResult | PromiseLike&lt;TResult&gt; where TResult = never. And then in my example a handler function returns 5 which is a number. So I could assume that 5 is never | PromiseLike&lt;never&gt;. But it is not the case. A number doesn't have .then function so it's not a PromiseLike.
Thanks!

答案1

得分: 1

TypeScript不会抱怨,因为catch()方法中的类型参数TResult在定义中只默认never。类型参数TResult没有以任何方式被限制never

请注意,在类型参数声明中的=运算符是默认类型参数,而类型参数声明中的extends运算符是类型参数约束。通常情况下,你可以选择使用它们中的任何一个或两者兼用。在调用签名中的类型参数声明,例如&lt;T extends C = D&gt;(⋯) =&gt; ⋯意味着T必须可分配给C,如果无法推断出T,它将默认为D(D也必须可分配给约束C)。如果省略了C,则等同于指定它为未知类型,如果省略了D,则等同于指定它为C。所以&lt;TResult = never&gt;等同于&lt;TResult extends unknown = never&gt;,意味着它默认为never,但没有任何约束。

所以:只有在无法从输入中推断出TResult时,才会回退到never,例如如果未提供onrejected参数。但是您确实提供了一个onrejected参数为() =&gt; 5。因此,TypeScript能够推断TResult类型参数为number,因为() =&gt; 5 是一个有效的 ((reason: any) =&gt; TResult | PromiseLike&lt;TResult&gt;) | undefined | null。您可以通过在启用 IntelliSense 的 IDE 中将鼠标悬停在catch调用上来验证这一点:

Promise.reject().catch(() =&gt; 5)
// (method) Promise&lt;never&gt;.catch&lt;number&gt;(
//    onrejected?: ((reason: any) =&gt; number | PromiseLike&lt;number&gt;) | null | undefined
// ): Promise&lt;number&gt;

所以一切都按照预期和意图工作。

英文:

TypeScript doesn't complain because the type parameter TResult in that definition of catch() method just defaults to never. The type parameter TResult is not constrained to never in any way.

Note that the = operator inside a type parameter declaration is a default type argument, while the extends operator inside a type parameter declaration is a type parameter constraint. In general you can have neither or either or both. A type parameter declaration on a call signature like &lt;T extends C = D&gt;(⋯) =&gt; ⋯ means that T must be assignable to C, and if T cannot be inferred, it will default to D (which must also be assignable to the constraint C). If you leave out C then it is equivalent to specifying it as the unknown type, and if you leave out D then it is equivalent to specifying it as C. So &lt;TResult = never&gt; is equivalent to &lt;TResult extends unknown = never&gt;, meaning it defaults to never but is not constrained at all.

So: TResult will only fall back to never in the event that it cannot be inferred from the input, such as if the onrejected argument is not supplied. But you did supply an onrejected argument of () =&gt; 5. And thus TypeScript was able to infer the TResult type argument as number, because () =&gt; 5 is a valid ((reason: any) =&gt; TResult | PromiseLike&lt;TResult&gt;) | undefined | null. You can verify this by hovering over the catch call in an IntelliSense-enabled IDE:

Promise.reject().catch(() =&gt; 5)
// (method) Promise&lt;never&gt;.catch&lt;number&gt;(
//    onrejected?: ((reason: any) =&gt; number | PromiseLike&lt;number&gt;) | null | undefined
// ): Promise&lt;number&gt;

So everything's working as expected and intended here.

答案2

得分: 0

由于回调函数返回值为5,catch() 创建的新 Promise 对象将以该值解决。也就是说,catch() 创建的新 Promise 对象将以值5解决,因为回调函数返回该值。

英文:

Since the callback function returns a value of 5, the new promise created by catch() is resolved with that value. Meaning, the new promise created by catch() is resolved with the value of 5 because the callback function returns that value.

huangapple
  • 本文由 发表于 2023年5月18日 03:57:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76275780.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定