泛型约束实战:封装一个类型安全的请求函数

举报
yd_275379215 发表于 2026/10/20 10:35:06 2026/10/20
【摘要】 项目里接口从二十个涨到两百个,每个调用点都在手写any强转。这次把请求层重构成泛型封装,调用点的类型体操全部消失,把设计过程记录下来。改造前的典型调用://返回值any,字段名写错只能运行时发现constuser=awaitrequest('/api/user/1')asanyconsole.log(user.namme)//拼错了也没人管第一步,定义后端统一响应结构并给...

项目里接口从二十个涨到两百个,每个调用点都在手写 any 强转。这次把请求层重构成泛型封装,调用点的类型体操全部消失,把设计过程记录下来。

改造前的典型调用:

// 返回值 any,字段名写错只能运行时发现
const user = await request('/api/user/1') as any
console.log(user.namme) // 拼错了也没人管

第一步,定义后端统一响应结构并给业务接口声明类型:

interface ApiResponse<T> {
  code: number
  message: string
  data: T // 真正的业务数据
}

interface UserInfo {
  id: number
  name: string
  role: 'admin' | 'member'
}

第二步,让 request 的泛型参数约束「返回类型描述」,而不是让调用方写死:

async function request<T>(
  url: string,
  options?: RequestInit
): Promise<T> {
  const res = await fetch(url, options)
  const json: ApiResponse<T> = await res.json()
  if (json.code !== 0) throw new Error(json.message)
  return json.data // 这里已经收窄为 T
}

// 调用点:只声明业务类型
const user = await request<UserInfo>('/api/user/1')
user.namme // 编译期立刻报错,Property 'namme' does not exist

第三步更进一步,用约束防止传入错误的 url。把所有接口路径建成一个映射类型:

interface Endpoints {
  '/api/user/1': { res: UserInfo }
  '/api/order/list': { res: Order[]; query: { status: number } }
}

function api<K extends keyof Endpoints>(
  url: K,
  ...args: Endpoints[K]['query'] extends never
    ? []
    : [query: Endpoints[K]['query']]
): Promise<Endpoints[K]['res']> {
  return request(url) as Promise<Endpoints[K]['res']>
}

api('/api/order/list', { status: 1 }) // 参数和返回值都全量校验
api('/api/user/2') // 报错:字面量路径不在 Endpoints 里

K extends keyof Endpoints 是整个设计的核心:泛型 K 被「约束」成只能是接口表里的 key,调用点写错路径直接红线。条件类型那行处理了「有的接口没有 query 参数」的差异,没有参数时连多余的实参都传不进来。

上线后的收益统计:两百多个调用点消灭了全部 as any,接口字段改名时编译器一次性列出所有受影响文件。维护成本是 Endpoints 表要与后端文档同步,我们用脚本从 Swagger 半自动生成,成本可控。泛型不是炫技,把「运行时才能发现的错」前移到编译期,这才是它该干的事。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。