You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 23:24:15