为什么 Typescript 需要 infer 关键字?

Why is the infer keyword needed in Typescript?

为什么 Typescript 的人创建了 infer 关键字? 根据 documents,这是您将如何使用它的示例:

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

我不明白为什么需要这个。为什么不能只是:

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

为什么这行不通?为什么需要 infer 关键字?

考虑以下代码:

interface Example {
    foo: string
}

type GenericExample<T> = T extends Examlep ? 'foo' : 'bar';

这段代码应该导致编译错误,因为Examlep拼写错误;没有名为 Examlep 的类型,显然程序员打算在这里写 Example

现在假设在条件类型的 extends 子句中不需要 infer 关键字。那么上面的代码就不会报编译错误了;它会看到没有名为 Examlep 的类型,推断它是什么类型,然后(因为 Examlep 没有约束)观察 T 确实扩展了 Examlep for推断的类型。

在那种情况下,GenericExample<T> 总是'foo' 而不管 T 是什么,并且不会有编译错误将错误告知程序员。这对编译器来说是错误的,几乎所有时候。

使用 infer,编译器确保您已明确声明所有类型变量 :

type MyType<T> = T extends infer R ? R : never;
type T1 = MyType<{b: string}> // T1 is { b: string; }

这里我们在MyType中声明了一个new类型变量R,其中gets inferred来自T.
请注意,infer 总是在 conditional typeextends 子句中使用。

现在使用未声明的类型参数会导致编译错误:

type MyType2<T> = T extends R2 ? R2 : never; // error, R2 undeclared

如果没有 infer,编译器将不知道,如果你想引入一个额外的类型变量 R2 来推断(见第一种情况),或者如果 R2 只是一次意外输入 error/typo。 infer 存在是为了消除这种歧义。

更准确地说,编译器会检查,如果 T is assignableR ,当 infer 被省略时:

type R = { a: number }
type MyType3<T> = T extends R ? R : never; // compare T with type R
type T2 = MyType2<{b: string}> // T2 is never

请注意,infer R 隐藏了同名类型声明的类型引用 R:

type R = { a: number }
type MyType<T> = T extends infer R ? R : never;
type T1 = MyType<{b: string}> // { b: string; }

Playground

infer 关键字允许您从条件类型中的另一个类型推导出一个类型。这是一个例子:

type UnpackArrayType<T> = T extends (infer R)[] ? R: T;
type t1 = UnpackArrayType<number[]>; // t1 is number

UnpackArrayType 是一种条件类型。它被读作“如果 T 是 (infer R)[] 的子类型,return R。否则,return T”。

对于类型别名 t1,UnpackArrayType 中的条件为真,因为 number[] 与 (infer R)[] 匹配。作为推断过程的结果,类型变量 R 被推断为数字类型,并且 return 从 true 分支。 Infer 是为了告诉编译器在 UnpackArrayType 的范围内声明了一个新的类型变量 R。

type t2 = UnpackArrayType<string>; //t2 is string

对于 t2 ,UnpackArrayType 中的条件为 false,因为字符串类型与 (infer R)[] 不匹配,因此它被 returned 为字符串。 有关更多信息,请查看这篇文章。 https://javascript.plainenglish.io/typescript-infer-keyword-explained-76f4a7208cb0?sk=082cf733b7fc66228c1373ba63d83187

我是这样想的:

  1. infer X 替换 any.

要使用上面的例子,

type UnpackArrayType<T> = T extends any[] ? T[number]: T;

->

type UnpackArrayType<T> = T extends (infer R)[] ? R: T;
  1. X声明为新类型,同时捕获
  2. X 现在可以用在条件的 true/false 部分。