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

TypeScript子类如何继承父类构造函数参数并修改后传递给super()?

问题描述

我有一个基类Sprite和子类Player。Sprite的constructor()接收若干参数,其中一个是图片路径。Player应接收除图片路径外的所有参数,并新增一个参数用于自行确定图片路径。

尝试使用ConstructorParameters实现,但代码无法运行:

// Sprite.ts
import SomeVendoredRenderLibrary from "./vendored/SomeVendoredRenderLibrary.js";

export default abstract class Sprite {
  private _image: HTMLImageElement;

  public constructor(
    protected canvasWidth: number,
    protected canvasHeight: number,
    imagePath: string
  ) {
    this._image = SomeVendoredRenderLibrary.loadImage(imagePath);
  }
}
// Player.ts
import Sprite from "./Sprite.js";

export default class Player extends Sprite {
  constructor(
    type: "modelA" | "modelB",
    ...args: ConstructorParameters<typeof Sprite>
  ) {
    // 此代码无法运行!
    args.imagePath = `assets/player_${type}.png`;
    super(...args);
  }
}

现存问题:

  • args是数组,无法通过属性名赋值
  • ConstructorParameters<typeof Sprite>要求显式传递imagePath,存在冗余
  • 显式复制父类参数到子类的做法维护成本高,父类参数变更时所有子类都要手动更新

请问继承父类构造函数参数并按需修改部分参数的规范做法是什么?


规范解决方案

方法1:解构父类构造参数,分离可变项

将父类构造参数拆分为公共参数和可变参数,子类接收公共参数+自定义参数后组合传给父类:

// Sprite.ts
import SomeVendoredRenderLibrary from "./vendored/SomeVendoredRenderLibrary.js";

export default abstract class Sprite {
  private _image: HTMLImageElement;

  // 定义公共参数类型,方便子类复用
  public type SpritePublicParams = Omit<ConstructorParameters<typeof Sprite>, 2>;

  public constructor(
    protected canvasWidth: number,
    protected canvasHeight: number,
    imagePath: string
  ) {
    this._image = SomeVendoredRenderLibrary.loadImage(imagePath);
  }
}
// Player.ts
import Sprite from "./Sprite.js";

export default class Player extends Sprite {
  constructor(
    type: "modelA" | "modelB",
    ...[canvasWidth, canvasHeight]: Sprite["SpritePublicParams"]
  ) {
    const imagePath = `assets/player_${type}.png`;
    super(canvasWidth, canvasHeight, imagePath);
  }
}

优点:父类参数顺序变更时,仅需更新SpritePublicParams类型,子类自动同步;避免冗余传递imagePath。

方法2:使用配置对象替代分散参数(推荐)

将父类构造参数改为单个配置对象,子类只需扩展配置或覆盖特定属性,这是最易维护的方式:

// Sprite.ts
import SomeVendoredRenderLibrary from "./vendored/SomeVendoredRenderLibrary.js";

export interface SpriteOptions {
  canvasWidth: number;
  canvasHeight: number;
  imagePath: string;
}

export default abstract class Sprite {
  private _image: HTMLImageElement;
  protected canvasWidth: number;
  protected canvasHeight: number;

  public constructor(options: SpriteOptions) {
    this.canvasWidth = options.canvasWidth;
    this.canvasHeight = options.canvasHeight;
    this._image = SomeVendoredRenderLibrary.loadImage(options.imagePath);
  }
}
// Player.ts
import Sprite, { SpriteOptions } from "./Sprite.js";

// 子类自定义参数,省略父类的imagePath
export interface PlayerOptions extends Omit<SpriteOptions, "imagePath"> {
  type: "modelA" | "modelB";
}

export default class Player extends Sprite {
  constructor(options: PlayerOptions) {
    super({
      ...options,
      imagePath: `assets/player_${options.type}.png`
    });
  }
}

优点:

  • 参数结构清晰,新增/修改父类参数时,子类无需大幅调整
  • 子类可灵活扩展或覆盖配置项,无需关注参数顺序
  • TypeScript类型检查更严格,避免参数顺序错误

方法3:利用类型工具提取父类参数(适合分散参数场景)

如果无法修改父类为配置对象,可以用ConstructorParameters结合数组解构精准替换参数:

// Player.ts
import Sprite from "./Sprite.js";

export default class Player extends Sprite {
  constructor(
    type: "modelA" | "modelB",
    ...args: Omit<ConstructorParameters<typeof Sprite>, 2>
  ) {
    const [canvasWidth, canvasHeight] = args;
    const imagePath = `assets/player_${type}.png`;
    super(canvasWidth, canvasHeight, imagePath);
  }
}

这里用Omit<ConstructorParameters<typeof Sprite>, 2>排除父类构造函数的第三个参数(索引为2),子类只需传递前两个参数+自定义的type参数,避免冗余。


内容的提问来源于stack exchange,提问作者theo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 04:00:34