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

TypeScript如何保留泛型约束提取泛型函数参数类型

提取泛型函数参数时保留类型约束的无冗余变量实现

问题场景

使用第三方库时存在如下类型定义:

type ParamsMap = {
  method1: {
    prop1: string,
  },
  method2: {
    prop1: number,
    prop2: string,
  }
}
type Methods = keyof ParamsMap;
type Query = <K extends Methods>(method: K, params: ParamsMap[K]) => void;

该库提供了上述类型的实现,典型调用模式如下:

const query: Query = (method, params) => {
  // 内部逻辑
  console.log(`${method} : ${JSON.stringify(params)}`);
};
query("method1", { prop1: "1234" });

需求是提取query函数的参数类型,用于在底层调用时增加额外处理逻辑,返回值类型无要求。

原生Parameters工具的缺陷

直接使用TypeScript内置的Parameters提取参数时,会丢失method与params之间的绑定约束:

/* 提取得到的类型:
[method: keyof ParamsMap, params: {
    prop1: string;
} | {
    prop1: number;
    prop2: string;
}]
*/
type ExtractQueryParams = Parameters<Query>
const test1 = (...params: ExtractQueryParams) => {
  console.log(`${JSON.stringify(params)}`);
}
// 非法传参不会触发报错:method1传入了method2的参数
test1("method1", { prop1: 1, prop2: "sdfs" });

该结果不符合类型安全要求,方法名和参数的对应关系失效。

现有方案的不足

目前可通过额外声明Query类型的占位变量,在typeof子句中传入泛型参数提取保留约束的类型:

let q: Query;
// 提取结果为 <K extends keyof ParamsMap> = [method: K, params: ParamsMap[K]]
type ExtractQueryParamsWithVar<K extends Methods> = Parameters<typeof q<K>>
const test2 = <K extends Methods>(...params: ExtractQueryParamsWithVar<K>) => {
  console.log(`${JSON.stringify(params)}`);
}
// 非法传参正常触发类型报错
test2("method1", { prop1: 1, prop2: "sdfs" });

该方案可以满足类型校验要求,但需要额外声明无实际用途的冗余变量。

无冗余变量的最优实现

场景化最简方案

针对当前固定的Query类型签名,不需要借助Parameters做类型提取,直接基于原类型的泛型约束构造参数元组即可,代码量最少且类型完全对齐:

// 直接构造保留约束的参数类型,无任何冗余声明
type QueryParams<K extends Methods> = [method: K, params: ParamsMap[K]];

const test3 = <K extends Methods>(...params: QueryParams<K>) => {
  console.log(`${JSON.stringify(params)}`);
}

// 非法传参正常报错
test3("method1", { prop1: 1, prop2: "sdfs" });
// 合法传参正常通过
test3("method1", { prop1: "valid" });
test3("method2", { prop1: 1, prop2: "valid" });

该方案完全复用了原Query类型的泛型约束逻辑,不存在类型偏差,也不需要声明任何占位变量。

通用泛型提取方案

如果需要适配任意泛型函数的参数提取,可通过条件类型推断实现通用工具类型,同样不需要额外变量:

// 通用泛型函数参数提取工具
type ExtractGenericParams<F, T> = F extends (arg1: infer A, ...args: infer R) => any
  ? T extends A
    ? [method: T, ...{ [I in keyof R]: R[I] extends Record<string, any> ? ParamsMap[T & keyof ParamsMap] : never }]
    : never
  : never;

// 针对Query类型使用
type GenericQueryParams<K extends Methods> = ExtractGenericParams<Query, K>;

对于绝大多数明确知道泛型函数签名结构的业务场景,直接构造参数元组的方案可读性和维护性更高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 22:09:17