网站搜索

ReturnType 辅助类型中的 TypeScript 类型推断


我无法理解为什么在下面的情况中 Temp 类型是 never。我理解为什么它与 (...args: any) => infer R 兼容,但它是如何被推断为 never 类型的呢?

// type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;


type Temp = ReturnType<never>;

答案#1

TL;DR 分配条件类型和 never 是空联合

长版本:

让我们首先理解为什么 ReturnType<never> 为 never 一开始会有点令人惊讶:

来自介绍 never 类型的 PR:

never 是每种类型的子类型并且可以赋值给每种类型。

所以这意味着如果我们问这个问题:never 扩展 AnyOtherType?答案应该永远是是的。那么让我们把这个问题问编译器:

type R = never extends (...args: any) => any ? "Y" : "N"; // Evaluates to "Y"

游乐场链接

所以这个定义确实成立,并且 never 是 (...args: any) => any 的子类型。但为什么 ReturnType<never> 会解析为 never 呢?如果推断失败,默认值通常是 unknown 而不是 never(参考)。让我们在 ReturnType 之外尝试这个推断:

type T0 = never extends (...args: any) => infer R ? R : any; //unknown 

游乐场链接

所以这里我们有一个非常令人惊讶的结果,T0 确实是 unknown,而 ReturnType<never> 是 never,尽管我们所做的只是将 ReturnType 的定义内联到 T0 中。

其原因是分配条件类型。对裸类型参数有条件的类型是分配类型。这意味着如果 T 是一个联合类型,每个联合成员都会独立地通过条件类型处理,所有独立的结果将被联合以得到最终结果。例如:

type T1 = ReturnType<(()=> number) | (() => string)> // number | string

编译器如何评估上面的类型:

ReturnType<(()=> number) | (() => string)>
  ->  ReturnType<()=> number> | ReturnType<() => string>
  ->  ((()=> number) extends (...args: any) => infer R) |  ((()=> string) extends (...args: any) => infer R)
  ->  number | string

好的,那么这与 never 有什么关系呢。实际上,never 是空联合类型(参考),一个没有任何成员的联合类型。这意味着当将 never 传入分布式条件类型时,条件类型实际上不会被求值,因为在尝试分发时,没有任何可分发的内容。

我们可以通过不让 T 直接使用,而是将其放在元组中,例如,来创建一个非分配版本的 ReturnType:

type ReturnTypeNonDistributive<T extends (...args: any) => any> = [T] extends [(...args: any) => infer R] ? R : any;
type Temp2 = ReturnTypeNonDistributive<never>; //unknown

游乐场链接