TypeScript类继承中实现等价于return new self的方案
问题背景
- 存在一个提供基础数组能力的基类
Collection,设计目的是被继承扩展,适配不同业务场景的集合操作 - 调用
filter这类生成新集合的方法时,要求返回当前实际调用类(而非固定基类)的新实例,以支持链式调用子类自定义方法,不能直接返回普通数组 - PHP中可通过
return new self()实现该能力,Java中对应写法为return obj.newInstance(),但JS/TS下未找到通用实现,临时方案是要求所有子类重写newInstance方法 - 现有实现的异常:
NumberCollection实例调用继承得到的filter方法后返回的是基类Collection实例,后续调用子类专属的sum()方法会抛出 sum() is not a function 错误
问题复现代码
class Collection<E> { protected items: E[]; constructor(items: any[] = []) { this.items = items; } // 目前的实现无法正确返回子类实例 protected newInstance(items: E[]) { return new Collection(items); //return new this.constructor(items); // TS报错:"This expression is not constructable" // return new this.constructor.prototype(items); // 运行时报错:"this.constructor.prototype is not a constructor" } size() { return this.items.length; } filter(callback: (item: any, index?: number) => boolean): this { return this.newInstance(this.items.filter(callback)) as this; } } class NumberCollection extends Collection<number> { sum() { return this.items.reduce((a, b) => a + b, 0); } } let numbers = new NumberCollection([1, 2, 3, 4]); console.log(numbers.sum()); // 正常运行 console.log(numbers.filter((n) => n > 1).sum()); // 抛出异常
通用解决方案
不需要子类重写newInstance方法,只需要修正newInstance中对构造函数的类型断言即可同时兼容JS运行时逻辑和TS类型校验。
修复后完整代码
class Collection<E> { protected items: E[]; constructor(items: E[] = []) { this.items = items; } protected newInstance(items: E[]): this { // 为constructor补充构造函数类型断言,通过TS校验 return new (this.constructor as new (items: E[]) => this)(items); } size() { return this.items.length; } filter(callback: (item: E, index?: number) => boolean): this { return this.newInstance(this.items.filter(callback)); } } class NumberCollection extends Collection<number> { sum() { return this.items.reduce((a, b) => a + b, 0); } } // 测试验证 const numbers = new NumberCollection([1, 2, 3, 4]); console.log(numbers.sum()); // 输出10 console.log(numbers.filter(n => n > 1).sum()); // 输出9,链式调用正常
实现原理
- JS运行时层面,实例的
constructor属性天然指向创建该实例的构造函数:子类实例的constructor就是子类本身,new this.constructor(items)在JS环境中本身就可以正确创建对应子类的实例 - TS的类型报错只是因为默认情况下
this.constructor的类型被推导为Function,没有声明构造签名,无法被TS识别为可构造的类型。通过类型断言明确指定该构造函数的入参类型和返回值类型(当前类实例this),即可通过类型检查 - 该方案对所有子类通用,不需要子类额外重写任何方法,所有继承
Collection的子类调用返回新集合的方法时,都会自动返回对应子类的实例,完整支持链式调用。
内容的提问来源于stack exchange,提问作者Dennis
相关产品推荐
相关产品推荐

