使用泛型TypeScript简化嵌套JSON,实现Person模型反序列化
用泛型TypeScript方案处理嵌套JSON反序列化,移除多余data层
问题背景
咱们先明确下需求:
- 接口返回的嵌套JSON结构:
{ "results": { "data": { "id":"93cba9bd-2969-3214-b26f-7f42d20241a4", "first_name":"PSU", "last_name":"Bot", "profile": { "data":{"about":"i am so happy"} } } } } - 目标
Person模型定义:export class Person extends BaseModel { first_name: string; last_name: string; profile: Profile; // 要求这里直接是Profile类型,不再包含data嵌套 } export class Profile extends BaseModel { about: string; }
泛型解决方案
咱们可以通过泛型工具类型定义类型映射规则,再配合递归转换函数实现数据结构的自动扁平化,具体步骤如下:
1. 定义泛型工具类型,解开嵌套的data层
首先写一个递归的工具类型,用来自动提取所有嵌套层级里的data属性值类型:
// 递归解开所有带data的嵌套结构 type UnwrapData<T> = T extends { data: infer U } ? UnwrapData<U> : T; // 定义API返回的通用结构类型 type ApiResponse<T> = { results: { data: T } };
这个UnwrapData类型会自动递归检查类型是否包含data属性,如果有就提取data的类型继续处理,直到没有嵌套的data为止。
2. 编写递归转换函数,处理实际数据
接下来写一个泛型函数,把API返回的嵌套数据转换成符合目标模型的结构:
function unwrapApiData<T>(response: ApiResponse<unknown>): UnwrapData<ApiResponse<unknown>> { // 递归遍历对象,解开所有data嵌套 function deepUnwrap(obj: unknown): unknown { // 如果当前对象有data属性,直接递归处理data的值 if (typeof obj === 'object' && obj !== null && 'data' in obj) { return deepUnwrap((obj as { data: unknown }).data); } // 处理数组:遍历每个元素递归解开 if (Array.isArray(obj)) { return obj.map(deepUnwrap); } // 处理普通对象:遍历每个属性递归解开 if (typeof obj === 'object' && obj !== null) { const unwrappedObj: Record<string, unknown> = {}; for (const key in obj) { unwrappedObj[key] = deepUnwrap((obj as Record<string, unknown>)[key]); } return unwrappedObj; } // 基础类型直接返回 return obj; } return deepUnwrap(response) as UnwrapData<typeof response>; }
3. 使用示例
现在就可以用这个函数处理API返回的数据,然后映射到Person模型:
// 模拟接口返回的数据 const apiResponse = { "results": { "data": { "id":"93cba9bd-2969-3214-b26f-7f42d20241a4", "first_name":"PSU", "last_name":"Bot", "profile": { "data":{"about":"i am so happy"} } } } }; // 解开嵌套的data层 const unwrappedData = unwrapApiData(apiResponse); // 此时unwrappedData的结构是:{ id: string; first_name: string; last_name: string; profile: { about: string } } // 映射到Person模型(如果BaseModel有构造逻辑,也可以在这里处理) const person = Object.assign(new Person(), unwrappedData); console.log(person.profile.about); // 输出:i am so happy
4. 优化类型精准度(可选)
如果想让转换函数的返回类型更精准,可以给unwrapApiData指定更明确的泛型约束:
type UnwrappedApiResult<T> = UnwrapData<ApiResponse<T>>; function unwrapApiData<T>(response: ApiResponse<T>): UnwrappedApiResult<T> { // 内部实现同上 }
这样一来,TypeScript就能更准确地推断转换后的数据类型,给你更完善的类型提示。
内容的提问来源于stack exchange,提问作者prog_24
相关产品推荐
相关产品推荐

