主要目标:我正在努力学习(并理解,而不仅仅是复制和粘贴)如何在Typescript中创建工厂,但在类型和类型推理方面存在一些困惑。我想有一个";MyCloner";类能够创建一个IClonable的多个实例。例如,如果我有一个Truck类和一个Motorcycle类,它们都实现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正在扩展函数";新的";(据我所知,这是一个保留关键字)。但是R、T和"new"是如何相互联系的呢?我不明白这个=>这里的运算符(它是用来声明函数的吗?)
在查了打字本之后,我不确定我是否理解推理在打字本中的作用。
以下是ExtractInstanceType和上下文的教程
我意识到保持卡车的电动化是额外的,据我所知,这不是工厂模式的一部分,但这是最终的目标。但我认为,在理解了推断和ExtractInstanceType的基础知识后,这应该不是一大步。
谢谢你抽出时间。
混淆的第二点(更多的类型声明和类型文字):
我也被同一教程中的下面一行弄糊涂了。
type userTypes = typeof userMap[Keys]; //typeof Developer | typeof Manager
对我来说,这似乎是在说钥匙不是一把钥匙?通常在JS中,我希望它是一个字符串,从字典中得到一个值作为回报?但键本质上是一个表示多个类型的类型文字,然后以某种方式用作单个键?
以下是供参考的密钥:
type Keys = keyof typeof userMap; // 'dev' | 'manager'
以后尽量把自己限制在每个问题一个问题,但问题的答案非常简洁,所以…
让我们将条件类型声明转换为一些更清楚地指示发生了什么的psuedo代码:
类型InstanceType(T)=如果T是一个类,则由
new
通过该类构造的事物的类型否则类型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满足它表示返回R
的可new
事物的约束",而R
只是"类T
构造的事物"。never
只是作为一种保护:以防调用方将类型与非类泛型参数一起使用不正确。
现在我们有了几乎语法有效的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
是可以用new
构造的类。注意,AFAIKinfer
只能用于此类条件类型的上下文中。
type Keys = keyof typeof userMap;
在这里,我们想要基于值userMap
中的键来构造类型。由于我们不能使用值作为类型,我们必须调用typeof
来获得userMap的类型,并且由于它是一个关联数据结构,我们可以调用keyof
来获得映射键的并集:'dev' | 'manager'
。
然后剩下的就就位了,我们可以使用索引类型从映射中获得值的类型的并集(在键/值对的意义上):
type userTypes = typeof userMap[Keys];
您可能会认为它是Developer | Manager
,但由于我们正在构建类型(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