泛型约束实战:封装一个类型安全的请求函数
【摘要】 项目里接口从二十个涨到两百个,每个调用点都在手写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)