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

能否让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 21:13:29