能否根据构造函数参数动态计算类属性?合并两类实现参数按需传入
合并响应类实现动态属性/参数
当然可以实现!你想要的是一个能根据传入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
相关产品推荐
相关产品推荐

