TypeScript如何实现返回类型与泛型一致的单对象/数组反序列化mapper
问题背景
我们需要对REST接口返回的JSON数据做反序列化,这类接口既可能返回单个对象,也可能返回多个同类型对象组成的数组。
接口返回示例
单个客户查询接口
请求路径:/api/customer/1
返回值:
{ "name": "Matt", "phone": "+010101010" }
多个客户查询接口
请求路径:/api/customer/
返回值:
[ {"name": "Matt", "phone": "+010101010"}, {"name": "John", "phone": "+99999999"}, {"name": "Doe", "phone": "+123131321"} ]
现有实现问题
当前已实现可用的反序列化函数如下:
const mapper = <T>(type: DTOResponse<T>, data: any): T | T[] | null => { let result: T | T[]; if (Array.isArray(data)) { result = data.map(x => plainToClass(type, x)); } else { result = plainToClass(type, data); } return result; } const resAsArrayObj = mapper<DtoModel>(DtoModel, dtoArray); const resAsSingleObj = mapper<DtoModel>(DtoModel, dtoSingleObj);
上述代码可正常运行,但缺点是返回值类型为联合类型T | T[],无法和传入的泛型参数直接对应。我们期望泛型传入单个类型时返回单个对象,泛型传入数组类型时返回对象数组。
解决方案
借助TypeScript条件类型实现泛型自动推导,改造后的代码如下:
// DTO类构造函数类型定义 type DTOResponse<T> = new () => T; // 泛型解包:如果T是数组类型则提取元素类型,否则返回T本身 type UnpackArray<T> = T extends (infer U)[] ? U : T; // 改造后的mapper函数 function mapper<T>(dtoClazz: DTOResponse<UnpackArray<T>>, data: any): T { if (Array.isArray(data)) { return data.map(item => plainToClass(dtoClazz, item)) as T; } return plainToClass(dtoClazz, data) as T; }
调用示例
完全符合预期的类型匹配效果:
// resAsArrayObj 类型自动为 DtoModel[] const resAsArrayObj = mapper<DtoModel[]>(DtoModel, dtoArray); // resAsSingleObj 类型自动为 DtoModel const resAsSingleObj = mapper<DtoModel>(DtoModel, dtoSingleObj);
内容的提问来源于stack exchange,提问作者Kaiser69
相关产品推荐
相关产品推荐

