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

React TypeScript处理不完整API响应的优雅方案咨询

解决REST API响应差异的TypeScript类型方案

一、基础类型拆分:从核心到扩展

先抽离所有接口共享的必填核心字段,再基于核心类型扩展不同场景的子集类型,嵌套结构也能复用这套逻辑:

// 核心必填字段:所有接口都会返回的基础数据
type TStudentCore = {
  firstName: string;
  lastName: string;
};

// 带好友列表的学生类型
type TStudentWithFriends = TStudentCore & {
  friends: TStudentWithFriends[];
};

// 完整学生类型
type TStudentFull = TStudentWithFriends & {
  mainTeacher: TTeacher[];
};

组件中直接使用对应类型:UserTable的props用TStudentCore,UserDetail用TStudentFull,明确约束不同场景的必填字段。

二、用TypeScript内置工具快速生成子集

如果不想手动拆分类型,可借助Pick/Omit等内置工具生成需要的子集,嵌套场景可配合递归工具类型:

// 列表接口返回类型:只取姓名字段
type TStudentList = Pick<TStudentFull, 'firstName' | 'lastName'>;

// 带好友的类型:排除mainTeacher字段
type TStudentWithFriends = Omit<TStudentFull, 'mainTeacher'>;

// 递归Pick工具:处理嵌套结构的字段选择
type RecursivePick<T, K extends keyof T> = {
  [P in K]: T[P] extends (infer U)[] 
    ? RecursivePick<U, keyof U>[] 
    : T[P] extends object 
      ? RecursivePick<T[P], keyof T[P]> 
      : T[P];
};

// 示例:只保留学生及好友的姓名字段
type TStudentListWithFriendsNames = RecursivePick<TStudentFull, 'firstName' | 'lastName' | 'friends'>;

三、类型守卫+运行时校验

API返回数据可能存在和类型声明不匹配的情况,尤其是嵌套结构,可结合Schema校验库(如zod)同时实现编译时类型安全和运行时数据校验:

import { z } from 'zod';

// 定义核心学生Schema
const StudentCoreSchema = z.object({
  firstName: z.string(),
  lastName: z.string(),
});

// 扩展带好友的Schema(处理嵌套递归)
const StudentWithFriendsSchema = StudentCoreSchema.extend({
  friends: z.lazy(() => StudentWithFriendsSchema.array()),
});

// 完整学生Schema
const StudentFullSchema = StudentWithFriendsSchema.extend({
  mainTeacher: z.array(z.object({ /* 教师字段定义 */ })),
});

// 自动生成对应的TypeScript类型
type TStudentCore = z.infer<typeof StudentCoreSchema>;
type TStudentWithFriends = z.infer<typeof StudentWithFriendsSchema>;
type TStudentFull = z.infer<typeof StudentFullSchema>;

// 请求后校验数据,确保和类型一致
async function fetchStudentList() {
  const res = await fetch('/api/students');
  const data = await res.json();
  return StudentCoreSchema.array().parse(data); // 校验失败会抛出错误
}

四、架构层面的DTO分层

将API返回的数据传输对象(DTO)和前端内部使用的业务实体类型分离,隔离API变化对业务逻辑的影响:

  • DTO层:对应每个API端点的返回结构,仅保留接口实际返回的字段;
  • 业务实体层:前端内部统一使用的完整类型,通过转换函数将DTO转换为业务实体(缺失字段可设默认值)。

示例:

// DTO:列表接口返回结构
type StudentListDTO = Pick<TStudentFull, 'firstName' | 'lastName'>;

// 业务实体:前端内部使用的完整类型
type Student = TStudentFull;

// DTO转业务实体的转换函数
function toStudent(dto: StudentListDTO): Partial<Student> {
  return {
    ...dto,
    friends: [],
    mainTeacher: [],
  };
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 17:02:34