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

能否根据构造函数参数动态计算类属性?合并两类实现参数按需传入

合并响应类实现动态属性/参数

当然可以实现!你想要的是一个能根据传入data的类型动态调整参数和属性的通用响应类,完全可以通过TypeScript的泛型、构造函数重载或者静态工厂方法来实现。下面我给你两种可行的方案:

方案一:构造函数重载 + 类型约束

这种方案直接通过构造函数的重载签名,让TypeScript在编译时就强制检查参数是否符合要求,同时搭配运行时校验避免类型绕过。

首先先定义Paging接口(根据你的实际需求补充属性):

interface Paging {
  page: number;
  size: number;
  total: number;
}

然后实现合并后的ResponseDto类:

type ResponseDtoInstance<T> = T extends any[] 
  ? { success: true; data: T; paging: Paging }
  : { success: true; data: T };

class ResponseDto<T> implements ResponseDtoInstance<T> {
  public readonly success = true;
  public data: T;
  public paging?: Paging;

  // 重载签名1:非数组类型的data,仅需传入data
  constructor(data: Exclude<T, any[]>);
  // 重载签名2:数组类型的data,必须传入data和paging
  constructor(data: T extends any[] ? T : never, paging: Paging);
  // 实际构造函数实现
  constructor(data: T, paging?: Paging) {
    this.data = data;
    
    // 运行时校验:如果data是数组但未传paging,抛出错误
    if (Array.isArray(data) && !paging) {
      throw new Error('Paging parameter is required when data is an array');
    }
    
    this.paging = paging;
  }
}

使用示例

// 非数组数据场景:仅传data即可
const singleItemResp = new ResponseDto({ id: 1, name: "Hugo" });
console.log(singleItemResp.data); // { id: 1, name: "Hugo" }
// singleItemResp.paging; // ❌ 编译报错:非数组场景下paging属性不存在

// 数组数据场景:必须传入data和paging
const listResp = new ResponseDto(
  [{ id: 1 }, { id: 2 }],
  { page: 1, size: 10, total: 2 }
);
console.log(listResp.data); // [{ id: 1 }, { id: 2 }]
console.log(listResp.paging); // { page: 1, size: 10, total: 2 }

// ❌ 编译报错+运行时错误:数组数据但未传paging
// const invalidResp = new ResponseDto([{ id: 1 }]);

方案二:静态工厂方法(推荐)

这种方案通过私有构造函数+静态工厂方法,更清晰地区分两种场景,类型约束更严格,也能避免用户错误调用构造函数。

interface Paging {
  page: number;
  size: number;
  total: number;
}

class ResponseDto<T> {
  public readonly success = true;
  public data: T;
  public paging?: Paging;

  // 私有构造函数,禁止直接实例化
  private constructor(data: T, paging?: Paging) {
    this.data = data;
    this.paging = paging;
  }

  // 创建非数组类型的响应
  static create<T>(data: T): ResponseDto<T> {
    if (Array.isArray(data)) {
      throw new Error('Please use `createIterable` method for array data');
    }
    return new ResponseDto(data);
  }

  // 创建数组类型的响应,强制要求传入paging
  static createIterable<T extends any[]>(data: T, paging: Paging): ResponseDto<T> {
    return new ResponseDto(data, paging);
  }
}

使用示例

// 非数组场景
const singleResp = ResponseDto.create({ id: 3, title: "Test" });

// 数组场景
const listResp = ResponseDto.createIterable(
  [{ id: 3 }, { id: 4 }],
  { page: 2, size: 10, total: 15 }
);

// ❌ 错误用法:数组数据调用create方法会抛出运行时错误
// const wrongResp = ResponseDto.create([{ id: 5 }]);

两种方案都能实现你想要的合并效果,方案二更符合TS的最佳实践,因为它通过明确的方法名区分场景,减少误用的可能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 21:08:11