ReturnType帮助器中的打字稿类型推断

时间:2020-07-04 18:04:17

标签: typescript type-inference

在以下情况下,我无法理解为什么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 个答案:

答案 0 :(得分:5)

TL; DR Distributive conditional typenever是空联合

长版:

首先让我们了解为什么ReturnType<never>成为never有点令人惊讶:

PR开始,介绍了never类型:

从不为每种类型的子类型,并且可以分配给每种类型。

所以这意味着如果我们问never extend AnyOtherType这个问题?答案应该永远是。因此,让我们向编译器询问这个问题:

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

Playground Link

因此,确实存在定义,并且never(...args: any) => any的子类型。但是,为什么ReturnType<never>解析为never?如果推断失败,则默认值通常是unknown而不是neverref)。让我们在ReturnType之外尝试这种推断:

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

Playground Link

所以这是我们产生令人惊讶的结果的地方,T0确实是unknown,而ReturnType<never>never,甚至认为我们所做的只是内联{ ReturnType中的{1}}。

原因是distributive conditional types。在裸类型参数上具有条件的类型是分布式的。这意味着,如果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实际上是一个空的联合(refernce),这是一个没有组成部分的联合。这意味着在通过分布式条件类型运行never时,实际上从不评估条件类型,因为在尝试进行分发时,没有任何内容可以分发。

我们可以在never上创建一个非分发版本,方法是不将ReturnType裸露,并将其放在一个元组中,例如:

T

Playground Link