能否让TypeScript从传入函数的Zod Schema自动推断泛型类型?
优化TypeScript中基于Zod的API路由自动类型推断
问题背景
当前实现中,使用Zod Schema为API路由提供静态类型校验和运行时保障,但调用applyRoute时必须手动传入泛型类型,存在类型不同步、参数顺序错误风险,且代码冗余。现有调用方式示例:
applyRoute<z.infer<typeof updateStaffMemberRoute.$PathParams>, z.infer<typeof updateStaffMemberRoute.$CallParams>>({ httpRequest, callParams: { name: 'John', age: 22, }, pathParams: { staffMemberId: staffMember.id, }, route: updateStaffMemberRoute, })
期望实现无需手动指定泛型,TypeScript自动从route对象的Zod Schema中推断参数类型,简化为:
applyRoute({ httpRequest, callParams: { name: 'John', age: 22, }, pathParams: { staffMemberId: staffMember.id, }, route: updateStaffMemberRoute, })
解决方案
核心思路是重构applyRoute的泛型定义,让TypeScript从传入的route对象自动提取Zod Schema对应的类型,而非手动传入泛型参数。具体修改如下:
1. 统一Route类型与实例的方法名
将示例路由实例中的getUrl改为getPath,与类型定义保持一致:
const updateStaffMemberRoute = { getPath(params: {staffMemberId: number}): string { return `staff_members/${params.staffMemberId}` }, method: 'POST', $CallParams: $UpdateStaffMemberCallParams, $PathParams: $UpdateStaffMemberUrlParams, }
2. 重构类型定义
调整Route和ApplyRouteParams类型,让参数类型自动从传入的路由对象中推断:
import { AxiosInstance, AxiosResponse } from 'axios'; import { z } from 'zod'; type RouteMethod = 'GET' | 'POST' | 'DELETE' | 'PATCH' | 'PUT'; type GetPathFunc<TPath> = (params: TPath) => string; // 基础Route类型,通过Zod Schema关联参数类型 type Route = { getPath: GetPathFunc<any>, method: RouteMethod, $CallParams: z.ZodType<any>; $PathParams: z.ZodType<any>; }; // 从传入的Route类型自动提取参数类型 type ApplyRouteParams<TRoute extends Route> = { httpRequest: AxiosInstance, callParams: z.infer<TRoute['$CallParams']>, pathParams: z.infer<TRoute['$PathParams']>, route: TRoute, };
3. 修改applyRoute函数实现
将泛型参数改为接收具体Route类型,自动推断所有关联类型:
function throwBadMethod(method: RouteMethod) { throw new Error(`unsupported value supplied for route method: ${method}`); } export function applyRoute<TRoute extends Route>( params: ApplyRouteParams<TRoute> ): Promise<AxiosResponse<z.infer<TRoute['$CallParams']>>> { const { route, httpRequest } = params; // 运行时校验参数 const pathParams = route.$PathParams.parse(params.pathParams); const callParams = route.$CallParams.parse(params.callParams); switch (route.method) { case 'GET': return httpRequest.get(route.getPath(pathParams), { params: callParams }); case 'POST': return httpRequest.post(route.getPath(pathParams), callParams); case 'PATCH': return httpRequest.patch(route.getPath(pathParams), callParams); case 'DELETE': return httpRequest.delete(route.getPath(pathParams), { data: callParams }); case 'PUT': return httpRequest.put(route.getPath(pathParams), callParams); default: throwBadMethod(route.method); } }
4. 简化调用
现在调用applyRoute时无需手动指定泛型,TypeScript会自动从路由对象中推断参数类型,同时提供静态校验:
applyRoute({ httpRequest, callParams: { name: 'John', age: 22, // 类型错误会即时提示,比如传字符串会报错 }, pathParams: { staffMemberId: staffMember.id, // 自动校验是否为正整数 }, route: updateStaffMemberRoute, })
关键说明
- 泛型参数
TRoute extends Route让TypeScript捕获传入的具体路由对象类型,通过z.infer提取Zod Schema对应的参数类型。 - 运行时依然通过Zod的
parse方法校验参数,保证静态类型与运行时逻辑一致。 - 修正了原代码中
getPath方法的参数引用错误(原示例直接使用staffMemberId而非params.staffMemberId)。
内容的提问来源于stack exchange,提问作者Willbill
相关产品推荐
相关产品推荐

