如何实现能创建类型变量类实例的TypeScript泛型类?
解决TypeScript泛型类中创建类型变量实例的问题
这个问题的核心在于TypeScript的泛型是擦除式的——编译成JavaScript后,泛型类型参数T会被完全移除,所以你在make()里写new T()的时候,运行时根本找不到T这个构造函数,自然会报错。
要解决这个问题,我们需要给Maker类提供一个实际的构造函数引用(也就是你要实例化的类本身),因为类在JavaScript里本质就是构造函数,我们可以把它保存起来,在make()方法里用这个保存的构造函数创建实例。
基础无参构造的实现
下面是修正后的完整代码:
class Thing { thingProp: string; } class ThingOne extends Thing { thingOneProp: string; } class ThingTwo extends Thing { thingTwoProp: string; } class Maker<T extends Thing> { // 保存传入的构造函数 private constructorFn: new () => T; // 构造函数接收T类型的构造器(无参) constructor(constructorFn: new () => T) { this.constructorFn = constructorFn; } make(): T { // 使用保存的构造函数创建实例 return new this.constructorFn(); } } // 使用时传入对应的类作为构造器,TypeScript会自动推导泛型类型 let thingOneMaker = new Maker(ThingOne); let thingOne: ThingOne = thingOneMaker.make(); let thingTwoMaker = new Maker(ThingTwo); let thingTwo: ThingTwo = thingTwoMaker.make(); // 这里依然会触发预期的类型错误,类型检查正常工作 // let thingError: ThingOne = thingTwoMaker.make(); // error TS2322: Type 'ThingTwo' is not assignable to type 'ThingOne'
编译后的JavaScript代码会正确保留构造函数的引用,运行时不会再出现ReferenceError:
var Maker = (function () { function Maker(constructorFn) { this.constructorFn = constructorFn; } Maker.prototype.make = function () { return new this.constructorFn(); }; return Maker; }()); var thingOneMaker = new Maker(ThingOne); var thingOne = thingOneMaker.make();
支持带参数构造的扩展
如果你的子类需要带参数的构造函数,我们可以调整构造器的类型定义,支持可变参数:
class Thing { constructor(public thingProp: string) {} } class ThingOne extends Thing { constructor(thingProp: string, public thingOneProp: string) { super(thingProp); } } class Maker<T extends Thing> { private constructorFn: new (...args: any[]) => T; constructor(constructorFn: new (...args: any[]) => T) { this.constructorFn = constructorFn; } // 传递参数给构造函数 make(...args: any[]): T { return new this.constructorFn(...args); } } let thingOneMaker = new Maker(ThingOne); let thingOne = thingOneMaker.make("base-prop", "one-prop"); console.log(thingOne.thingProp); // 输出 "base-prop" console.log(thingOne.thingOneProp); // 输出 "one-prop"
核心原理总结
TypeScript的泛型仅在编译阶段提供类型检查,运行时没有泛型类型的信息。所以我们必须通过传递实际的构造函数来让运行时知道要实例化哪个类,这样既保留了TypeScript的类型安全,又能在运行时正确创建实例。
内容的提问来源于stack exchange,提问作者Daniel Griscom
相关产品推荐
相关产品推荐

