使用 infer 和 typeof 在 Typescript 中创建工厂

Creating a Factory in Typescript using infer and typeof

主要目标: 我正在尝试学习(并理解,而不仅仅是复制和粘贴)如何在 Typescript 中创建工厂,但有一些与类型和类型推断相关的混淆点。我想 有一个“MyCloner”class 能够创建一个 IClonable 的多个实例。例如,如果我有一辆卡车 class 和一辆摩托车 class 都实现了 IClonable.

我希望能够做类似的事情:

const vehicleCloner = new MyCloner();
const truck = new Truck({color: 'red', fuel: 'electric'});

和 MyCloner 做类似的事情:

var myTenElectricTrucks = vehicleCloner.cloneWithRandomColors(truck, 10);

第一个混淆点(推断和新): 我一直在关注一些教程,但我并不完全理解这一点:

type ExtractInstanceType<T> = T extends new () => infer R ? R : never;

我想我不习惯这种类型的声明,一切都在进行。我知道我们正在声明某种类型,看起来能够采用称为 T 的泛型。然后看起来 T 正在扩展函数“new”(据我所知这是一个保留关键字)。但是 R、T 和 'new' 是如何相互关联的呢?我不明白这里的 => 运算符是怎么回事(它是用来声明函数的吗?)

我不确定我在查找后是否理解推断在 Typescript 中的作用。

这是 the tutorial for ExtractInstanceType和上下文

我意识到将卡车保持为电动是额外的,据我所知通常不是工厂模式的一部分,但这是最终目标。但我认为,在了解了推断和 ExtractInstanceType 的基础知识之后,这应该不会是一个很大的进步。

感谢您的宝贵时间。

第二个混淆点(更多的类型声明和类型字面量):

我也对 the same tutorial 中的以下行感到困惑。

type userTypes = typeof userMap[Keys]; //typeof Developer | typeof Manager

对我来说,这似乎是在说 Keys 不是单个键?通常在 JS 中,我希望它是一个字符串,可以从字典中获取 return 中的单个值?但关键本质上是一个表示多种类型的类型文字,然后以某种方式用作单个键?

这里有Key供参考:

type Keys = keyof typeof userMap; // 'dev' | 'manager'

以后尝试将自己限制为每个问题一个问题,但您的问题的答案非常简洁,所以...

让我们将该条件类型声明转换为一些伪代码,以更清楚地指示正在发生的事情:

type InstanceType(T) = if T is a class then the type of the thing constructed by newing that class else type never

让我们逐渐回到实际代码(这些都是同一事物的表示):

type InstanceType<T> = (T is a class) ? (thing-T-constructs, i.e. 'R') : never
type InstanceType<T> = (T extends (new () => R)) ? R : never

希望逐行查看替换(例如简化数学方程式)会有所帮助。 T extends new () => R 就是 'T satisfies the constraint that it represents a newable thing that returns an R' 而 R 就是 'the thing the class T constructs'。 never 只是作为一种保障:以防调用者错误地使用非 class 泛型参数的类型。

现在我们有 几乎 句法有效的 Typescript。但问题是 R:它是一个未声明的变量,在类型中和在代码中一样存在问题。这就是 infer 关键字的用武之地:

type InstanceType<T> = (T extends (new () => infer R)) ? R : never
// and dropping the grouping parens to arrive back at the original:
type InstanceType<T> = T extends new () => infer R ? R : never

这里我们告诉编译器根据满足的条件推断R的类型,即T是一个class 可以用 new 构造。 N.B。 AFAIK infer 只能 用于像这样的条件类型的上下文中。

type Keys = keyof typeof userMap;

这里我们要根据valueuserMap中的key构造一个type。由于我们不能将值用作类型,因此我们必须调用 typeof 来获取 userMap 的类型,并且由于它是关联数据结构,我们可以调用 keyof 来获取 联合 的地图键:'dev' | 'manager'.

然后剩下的就到位了,我们可以使用索引类型来获取类型的并集(在 key/value 对意义上)来自地图:

type userTypes = typeof userMap[Keys];

您可能会认为是 Developer | Manager,但由于我们正在构造一个 type (userTypes) 并且我们不能将值用作类型, 实际并集是 typeof Developer | typeof Manager.

总结第二部分:

typeof userMap;       // Typescript compile-time type of the userMap
keyof typeof userMap; // The type of the *compile-time keys* of userMap
typeof userMap[keyof typeof userMap] // ditto but for the types of the *values* in the map