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

如何在TypeScript中编写无重复代码的构造函数?

简化TypeScript子类构造函数的冗余属性定义

方案1:使用工具类型提取非方法实例属性

先定义一个通用工具类型,过滤掉类中的方法属性,只保留实例数据属性:

// 提取类的非方法实例属性
type InstanceProps<T> = {
  [K in keyof T as T[K] extends Function ? never : K]: T[K];
};

接着修改子类构造函数,利用该类型定义参数,结合Object.assign批量赋值属性:

class A {
  foo: string;

  constructor({ foo }: { foo: string }) {
    this.foo = foo;
  }

  equals(other: A) {
    return this.foo === other.foo;
  }
}

class B extends A {
  bar: number;
  // 示例其他属性,实际场景可扩展至20个
  baz: boolean;
  qux: string;

  constructor({ foo, ...rest }: InstanceProps<B>) {
    // 优先调用父类构造函数传入所需属性
    super({ foo });
    // 批量赋值子类自有属性,无需逐个手动赋值
    Object.assign(this, rest);
  }
}

const b = new B({
  foo: 'Test',
  bar: 42,
  baz: true,
  qux: 'hello'
});

原理说明

  • InstanceProps<T>通过条件类型筛选,排除了类中所有方法类型的属性,解决了直接用B类型作为参数时包含equals等方法的类型冲突问题。
  • 解构参数时分离出父类需要的属性,剩余属性通过Object.assign一次性绑定到实例,彻底消除了属性重复定义的冗余。

方案2:提前定义属性接口(更直观)

如果觉得工具类型不够直观,可以提前定义包含所有属性的接口,让类实现该接口,构造函数直接使用接口作为参数类型:

// 集中定义所有属性
interface BProps {
  foo: string;
  bar: number;
  baz: boolean;
  qux: string;
  // 其他20个属性...
}

class A {
  foo: string;

  constructor({ foo }: { foo: string }) {
    this.foo = foo;
  }

  equals(other: A) {
    return this.foo === other.foo;
  }
}

class B extends A implements BProps {
  bar: number;
  baz: boolean;
  qux: string;

  constructor(props: BProps) {
    super({ foo: props.foo });
    Object.assign(this, props);
  }
}

优势

  • 属性接口可单独复用(比如用于API请求参数、数据传输对象等场景)。
  • 属性定义集中在接口,类的属性声明只需与接口保持一致,降低后续维护成本。

注意事项

  • 使用Object.assign时,确保类中已声明所有需要赋值的属性类型,否则TypeScript会抛出类型错误。
  • 如果父类包含多个需要传入的属性,可通过解构InstanceProps<A>获取父类所需属性集合,避免硬编码属性名。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:05:07