嗨,大家好!这里是道长王jj~ 🎩🧙♂️
在面向对象编程(OOP)中,通常我们会定义接口,并在不同的类中实现这些接口。但在 TypeScript 中,我们也可以进行反向操作,即从一个类中派生一个接口,而无需实际定义这个接口。这种做法在多种场景下非常有用,比如为 JavaScript 类创建类型安全的模拟。这能确保模拟类实现了原始类的所有方法,而无需额外定义独立的接口。
提取实例类型
首先,我们需要从类中提取实例类型。默认情况下,当你在 TypeScript 中检索类的类型时,你会获得类的构造函数类型,而不是该类实例的类型。为了获得实例类型,我们可以定义一个实用型:
type ExtractInstanceType<T> = T extends new (...args: any[]) => infer R ? R : T extends {
prototype: infer P } ? P : any;
这里的 ExtractInstanceType<T>
首先尝试从类的构造函数中推断实例类型。如果构造函数不可公开访问,它会从类的原型属性中推断实例类型。这与 TypeScript 内置的 InstanceType<T>
工具类型的主要区别在于,后者只适用于公开的构造函数。
考虑以下带有私有构造函数的类:
class SomeClass {
private constructor() {
}
someMethod() {
}
}
//> OK: 类型为 SomeClass
type SomeClassInstance = ExtractInstanceType<typeof SomeClass>;
//> Error: 类型 'typeof SomeClass' 无法满足约束 'abstract new (...args: any) => any'。
type SomeClassInstanceError = InstanceType<typeof SomeClass>
从实例类型中提取方法
一旦我们有了实例类型,我们可以提取出它的方法。为此,我们可以定义另外两个实用型:
type ExtractMethodNames<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never }[keyof T];
type ExtractMethods<T> = Pick<T, ExtractMethodNames<T>>;
ExtractMethodNames<T>
是一个映射类型,它遍历 T
的所有属性,并检查每个属性是否对应于一个方法。如果是方法,则保留该属性的键;否则,将该键分配为 never
类型。
ExtractMethods<T>
利用 Pick
工具类型从 T
中仅选择那些属于方法的属性。
现在,让我们使用这些实用型从 SomeClassInstance
中提取方法:
type SomeClassMethods = ExtractMethods<SomeClassInstance>;
在这个示例中,SomeClassMethods
是仅包含 SomeClass
实例方法的类型。
使用提取的方法实现类
最后,我们来看看如何定义一个实现了 SomeClassMethods
类型的新类。通过确保类的类型为 SomeClassMethods
,我们可以确保该类具有与 SomeClass
实例相同的方法。
//> OK: 类实现了 SomeClass 中的方法
class SomeClassMock implements SomeClassMethods {
someMethod() {
}
}
//> Error: 类 'SomeClassMockError' 不正确地实现了接口 'SomeClassMethods'。
//> 类型 'SomeClassMockError' 中缺少属性 'someMethod',但在类型 'SomeClassMethods' 中是必需的
class SomeClassMockError implements SomeClassMethods {
anotherMethod() {
}
}
在这段代码中,SomeClassMock
是一个实现了 SomeClassMethods
类型的新类。这确保了 SomeClassMock
包含了 someMethod
函数。如果 SomeClassMock
不包含 someMethod
函数,或者 SomeClassMock
中的 someMethod
函数与 SomeClassMethods
中的函数不匹配,TypeScript 将引发编译错误。🛠️
🎉 你觉得怎么样?这篇文章可以给你带来帮助吗?当你处于这个阶段时,你发现什么对你帮助最大?如果你有任何疑问或者想进一步讨论相关话题,请随时发表评论分享您的想法,让其他人从中受益。🚀✨