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

如何通过父类ModelCore构造函数为子类动态设置指定属性值?

动态过滤并赋值子类属性的泛型父类问题

需求

实现一个全局父类,让子类继承后,自动过滤服务返回的数据,只保留子类中定义的属性,开发者无需手动处理返回值。

子类示例代码

export class EnderecoMobile extends ModelCore{
  id: string = ''
  cep: number = 1

  constructor(data){
    super(data)
  }
}

export class ComandadasMobile extends ModelCore {
  postId: number = 1
  id: number = 1
  name: string = ""

  constructor(data){
    super(data)
  }
}

当前实现的ModelCore类

export class ModelCore {

  constructor(data: any, canal?: string) {
    const device: RegExp = new RegExp(this.getDevice(), 'i');
    let string = this.constructor.toString().replace(/(\n|\r)/g, '');

    if (device.test(this.constructor.name)) {
        this.montaResposta(data, string)
    }
  }
  
  private getDevice() {
    const regex = /(mobile|desktop)/i;
    const device = this.constructor.name.match(regex);
    return device ? device[0] : undefined;
  }

  private montaResposta(data: any, string: string) {
    Object.keys(data).forEach(key => {
      if (typeof data[key] === 'object') this[key] = this.preencherObjeto(data[key], string);
      if (Array.isArray(data[key])) this[key] = this.preencherArray(data[key], string);
      else
        this.setaVariavel(data, key, string);
    });
  }

  private preencherArray(data: any, string: string): any {
    Object.keys(data).forEach(element => {
      if (typeof data[element] === 'object') {
        this.preencherObjeto(data[element], string);
      } else {
        if (Array.isArray(data[element])) { this.preencherArray(data[element], string) }
        else {
          let key = Object.keys(data[element])[0];
          this.setaVariavel(data[element], key, string);
        }
      }
    })
  }

  private preencherObjeto(obj: any, string: string) {
    return Object.keys(obj).forEach(key => {
      this.setaVariavel(obj, key, string);
    })
  }

  private setaVariavel(data: any, key: any, string: string) {
    const js: RegExp = new RegExp(` this\.${key} `);
    if (this.hasOwnProperty(key) || js.test(string)) { this[key] = data[key]; } else { delete data[key] }
  }
}

测试问题

测试1:EnderecoMobile传入对象

输入数据:

{
  id: '2',
  cep: 57057102,
  number: '278'
} 

预期结果:

{ id: '2', cep: 57057102 }

实际结果:

{ "id": "", "cep": 1 }

测试2:ComandadasMobile传入数组

输入数据:

[
  {
    "postId": 1,
    "id": 1,
    "name": "id labore ex et quam laborum",
    "email": "Eliseo@gardner.biz",
    "body": "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium"
  },
  {
    "postId": 1,
    "id": 2,
    "name": "quo vero reiciendis velit similique earum",
    "email": "Jayne_Kuhic@sydney.com",
    "body": "est natus enim nihil est dolore omnis voluptatem numquam\net omnis occaecati quod ullam at\nvoluptatem error expedita pariatur\nnihil sint nostrum voluptatem reiciendis et"
  },
  {
    "postId": 1,
    "id": 3,
    "name": "odio adipisci rerum aut animi",
    "email": "Nikita@garfield.biz",
    "body": "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione"
  },
]

实际结果:

{
    "0": {
        "postId": 1,
        "id": 1,
        "name": "id labore ex et quam laborum"
    },
    "1": {
        "postId": 1,
        "id": 2,
        "name": "quo vero reiciendis velit similique earum"
    },
    "2": {
        "postId": 1,
        "id": 3,
        "name": "odio adipisci rerum aut animi"
    },
}

问题分析与修复方案

核心问题

  1. 设备判断逻辑错误:getDevice返回匹配到的设备字符串后,用它去测试类名,导致montaResposta方法根本没执行,子类属性保持默认值。
  2. 正则解析类定义不可靠:通过this.constructor.toString()解析属性的方式容易出错,且没必要。
  3. 数组处理逻辑错误:preencherArray没有返回新数组,而是直接将元素挂载到实例上,导致数组变成键值对对象。
  4. 嵌套对象/数组未正确创建子类实例:嵌套结构没有生成对应子类的实例,无法递归过滤属性。

修复后的泛型ModelCore实现

export class ModelCore<T extends ModelCore<T>> {
  constructor(data?: Partial<T>) {
    // 如果需要保留仅处理mobile/desktop后缀类的逻辑,取消下面两行注释
    // const isTargetClass = /(mobile|desktop)/i.test(this.constructor.name);
    // if (!data || !isTargetClass) return;
    
    if (!data) return;
    
    // 获取当前子类实例的所有属性(即子类定义的属性)
    const ownProps = Object.keys(this) as Array<keyof T>;
    
    for (const key of ownProps) {
      const value = data[key];
      if (value === undefined) continue;
      
      // 处理数组:递归创建子类实例
      if (Array.isArray(value)) {
        this[key] = value.map(item => 
          typeof item === 'object' && item !== null ? new (this.constructor as new (data: Partial<T>) => T)(item) : item
        ) as T[keyof T];
      } 
      // 处理对象:递归创建子类实例
      else if (typeof value === 'object' && value !== null) {
        this[key] = new (this.constructor as new (data: Partial<T>) => T)(value) as T[keyof T];
      } 
      // 基础类型直接赋值
      else {
        this[key] = value as T[keyof T];
      }
    }
  }
}

修复说明

  1. 泛型支持:用T extends ModelCore<T>约束子类类型,确保类型安全。
  2. 简化判断逻辑:去掉不可靠的正则解析类定义,直接通过Object.keys(this)获取子类实例的属性,精准过滤。
  3. 正确处理数组/嵌套对象:数组元素和嵌套对象都会创建当前子类的实例,递归过滤属性,同时保持数组结构。
  4. 可配置的类过滤:如果需要仅处理带mobile/desktop后缀的类,可取消注释对应的判断代码。

测试验证

  • 给EnderecoMobile传入测试对象,会正确覆盖默认值,过滤掉number属性,得到预期结果。
  • 给ComandadasMobile传入测试数组,会返回包含ComandadasMobile实例的数组,每个实例仅保留postId、id、name属性,且保持数组结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 18:12:52