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

TypeScript中如何跳过泛型函数类型显式定义?API请求场景

TypeScript 泛型函数类型优化方案

问题背景

我们正在为API请求构建一个泛型函数类型,该类型支持payload、response、query parameters、path parameters和config等多个可选泛型参数,代码如下:

export type ApiServiceBaseMethod<
  P = never,
  R = void,
  Q extends QueryParams | undefined = Record<string, string | number>,
  M extends PathParams | undefined = Record<string, string | number>,
  C extends ApiServiceMethodConfig = Record<string, string>,
> = ({
  payload,
  pathParams,
  queryParams,
  config,
}: {
  payload?: P;
  pathParams?: M;
  queryParams?: Q;
  config?: C;
}) => Promise<AxiosResponse<R>>;

当前痛点在于:如果仅需指定泛型M(路径参数)的类型,必须手动设置它之前的所有泛型类型为unknown/undefined/never,操作繁琐。由于TypeScript暂不支持跳过泛型参数,需要找到无需定义特定子函数类型变体的解决方法。

解决方案一:用配置对象作为单一泛型参数

将多个泛型参数合并为一个可选的配置对象类型,按需指定需要的泛型属性,无需按顺序填充前面的参数:

// 定义泛型配置的基础结构
type ApiMethodGenerics = {
  P?: never;
  R?: void;
  Q?: QueryParams | undefined;
  M?: PathParams | undefined;
  C?: ApiServiceMethodConfig;
};

// 处理默认值的工具类型
type ResolveApiMethodGenerics<T extends ApiMethodGenerics = {}> = {
  P: T['P'] extends never ? never : T['P'];
  R: T['R'] extends void ? void : T['R'];
  Q: T['Q'] extends QueryParams | undefined ? T['Q'] : Record<string, string | number>;
  M: T['M'] extends PathParams | undefined ? T['M'] : Record<string, string | number>;
  C: T['C'] extends ApiServiceMethodConfig ? T['C'] : Record<string, string>;
};

// 重构后的泛型函数类型
export type ApiServiceBaseMethod<T extends ApiMethodGenerics = {}> = ({
  payload,
  pathParams,
  queryParams,
  config,
}: {
  payload?: ResolveApiMethodGenerics<T>['P'];
  pathParams?: ResolveApiMethodGenerics<T>['M'];
  queryParams?: ResolveApiMethodGenerics<T>['Q'];
  config?: ResolveApiMethodGenerics<T>['C'];
}) => Promise<AxiosResponse<ResolveApiMethodGenerics<T>['R']>>;

使用示例

// 仅指定路径参数类型
type GetUserMethod = ApiServiceBaseMethod<{ M: { userId: string } }>;

// 使用该类型
const getUser: GetUserMethod = async ({ pathParams }) => {
  // pathParams会被推断为{ userId: string } | undefined
  return axios.get(`/users/${pathParams?.userId}`);
};

// 指定多个泛型参数的示例
type CreateUserMethod = ApiServiceBaseMethod<{
  P: { name: string; email: string };
  R: { userId: string; createdAt: Date };
}>;

解决方案二:利用交叉类型覆盖默认泛型

通过交叉默认泛型和自定义泛型的方式,简化类型定义:

// 定义带有默认值的基础泛型集合
type DefaultApiGenerics = {
  P: never;
  R: void;
  Q: Record<string, string | number>;
  M: Record<string, string | number>;
  C: Record<string, string>;
};

export type ApiServiceBaseMethod<T extends Partial<DefaultApiGenerics> = {}> = ({
  payload,
  pathParams,
  queryParams,
  config,
}: {
  payload?: (T & DefaultApiGenerics)['P'];
  pathParams?: (T & DefaultApiGenerics)['M'];
  queryParams?: (T & DefaultApiGenerics)['Q'];
  config?: (T & DefaultApiGenerics)['C'];
}) => Promise<AxiosResponse<(T & DefaultApiGenerics)['R']>>;

使用示例

// 仅指定路径参数类型
type GetUserMethod = ApiServiceBaseMethod<{ M: { userId: string } }>;

这种方式利用TypeScript交叉类型的特性,自定义泛型会覆盖对应位置的默认值,未指定的属性则保留原默认类型。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 09:40:14