在Typescript中,never类型是如何工作的?

在Typescript中,never类型是如何工作的?,typescript,Typescript,我经常偶然发现从不打字脚本。虽然我已经阅读了文档,查看了关于SO的现有问题,但我的理解仍然不是很好。我希望有人能帮助我更好地理解它。以下是TS文档中让我头晕目眩的一个示例: type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : never; type Num = GetReturnType<() => number>; type S

我经常偶然发现
从不
打字脚本。虽然我已经阅读了文档,查看了关于SO的现有问题,但我的理解仍然不是很好。我希望有人能帮助我更好地理解它。以下是TS文档中让我头晕目眩的一个示例:

type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
  ? Return
  : never;

type Num = GetReturnType<() => number>;

type Str = GetReturnType<(x: string) => string>;

type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;
type GetReturnType=type扩展(…args:never[])=>推断返回
? 返回
:从不;
type Num=GetReturnType number>;
类型Str=GetReturnType字符串>;
类型Bools=GetReturnType boolean[]>;
我的理解是,指定
(…args:never[])=>…
意味着创建一个不接受任何参数的函数签名(为什么
never[]
?)。然而,
Str
Bools
都是带有参数的函数签名(这难道不应该出错吗?)


我也在努力理解条件类型:
inferreturn?返回:从不
。对我来说,这意味着函数要么有一个返回值,要么编译器应该产生一个错误。但我不确定。

通常
从不
的意思是“在这个范围内,这种类型不能使用”。这在实例化不需要知道类型的泛型参数时非常有用。在这种情况下,要获取函数的返回值,其参数无关紧要。因此可以安全地将它们键入为
从不

<> >代码> NO[[]/COD> >这里表示“某种类型的数组,请考虑它是一种类型错误,假设它是任何特定类型”。< /P> 在这种情况下,这比
any[]
更可取,因为
any
允许您对该类型执行任何操作,并且通常使用它意味着失去类型安全性

除了泛型类型别名之外,它就没什么用处了。如果您试图更直接地使用它:

type Fn = (...args: never[]) => string
const fn: Fn = (a: number, b: string, c: boolean) => a.toFixed()
fn(123) // Argument of type 'number' is not assignable to parameter of type 'never'.(2345)
它允许赋值的原因与
GetReturnType
工作的原因相同,但是当您尝试使用或引用
never
时,您将得到一个类型错误


问题的第二部分是完全不同的事情。首先,您的逻辑分组不正确。更像是:

(Type extends (...args: never[]) => infer Return) ? Return : never
或在psuedo代码中:

if (Type is subtype of ((...args: never[]) => infer Return) {
  use type Return
} else {
  use type never
}
expert
允许您从较大的类型中提取深度嵌套的类型,但它仅在
a扩展B
样式中工作。当您看到
expert
时,请将该类型视为带有通配符的模式。通配符将被推断为您提供的任何名称的类型,然后它将在三元函数的正端可用

一个简单的例子:

type HasCoolness<T extends { [key: string]: unknown }> = T extends { coolness: infer R } ? R : never
type TestA = HasCoolness<{ coolness: number }> // number
type TestB = HasCoolness<{ coolness: string }> // string
type TestC = HasCoolness<{ notCool: 'ugh' }> // never
同样,除了推断属性之外,它是推断函数的返回类型。请注意,由于
类型
不受约束(即它不是
GetReturnType
),因此您可以向它传递任何内容。但是如果你传递的不是一个函数,它将不会终止模式,并且会使用三元数的否定子句,这将导致
从不

从不
是一个错误。这意味着
never
是一个没有值的类型,它是其他所有类型的子类型。如果您想象一个类型对应于一组可分配给该类型的值,那么
从不
对应于该类型

我的理解是,指定
(…args:never[])=>…
意味着创建一个不接受任何参数的函数签名(为什么
never[]
?)

从技术上讲,你几乎是正确的;类型为
never[]
的数组不能包含任何值,但空数组可分配给类型为
never[]
,因为类型为
never
。因此从技术上讲,
(…args:never[])=>…
类型的函数可以用零参数调用

也就是说,这不是真正的情况。要理解
T扩展(…args:never[])=>推断R
,我们需要讨论。为了便于解释,我们假设我们有两种类型,
Animal
Dog
,而
Dog
Animal
的一个子类型:

    <> L>>P>首先考虑函数类型<代码>()= >动物< /代码>和<代码>(>)>狗< /代码>,我们将分别称之为代码> GETBASE< <代码>和<代码> GETBOT/<代码>。一个
    GetAnimal
    是一个函数,可以调用它来获取一个
    Animal
    ;可以调用
    GetDog
    来获取
    Animal
    ,因为
    Dog
    Animal
    ,因此(由)
    GetDog
    GetAnimal
    的子类型。这意味着函数类型与其返回类型是协变的,因为如果用子类型替换函数的返回类型,则会得到原始函数类型的子类型。(反之亦然,如果将返回类型替换为超类型,则会得到原始函数类型的超类型。)

    <> L>>P>现在考虑类型<代码>(A:动物)>空< <代码>和<代码>(A:狗)=空< <代码>,我们将分别称为<代码> TakeAnimal < /代码>和<代码> TakeDog < /代码>。
    TakeDog
    是一个不一定能接受任何
    Animal
    的函数,因此您不能在任何需要
    TakeAnimal
    的地方使用
    TakeDog
    ;这意味着
    TakeDog
    不是
    TakeAnimal
    的子类型。另一方面,
    TakeAnimal
    是一个可以接受任何类型动物的函数,包括
    Dog
    ,因此它是一个可以接受
    Dog
    的函数,因此(再次通过LSP)
    TakeAnimal
    TakeDog
    的子类型。这意味着函数类型与其参数类型相反,因为如果用超类型替换函数的参数类型,则会得到原始函数类型的子类型。(反之亦然,如果将参数类型替换为子类型,则会得到原始函数类型的超类型。)

这归结为函数类型
(…args:never[])=>R
type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
  ? Return
  : never
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any