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

如何基于泛型参数值获取TypeScript函数的参数类型?

泛型函数参数类型的动态推导问题

在TypeScript中,我们可以让函数参数的类型依赖于另一个泛型参数,比如:

// foo(true, (v /* 类型为1 */) => void 0)
// foo(false, (v /* 类型为2 */) => void 0)
const foo = <T extends boolean>(a: T, cb: (v: T extends true ? 1 : 2) => void) => {}

另外,我们可以通过typeof获取函数类型,再用Parameters<typeof foo>提取它的参数类型:

const foo = (a: number, b: string) => {}
// baz的回调参数类型和foo的参数类型完全一致
const baz = (cb: (...args: Parameters<typeof foo>) => void) => {}

但如果面对的是泛型函数且原函数定义未知的情况,该怎么实现类似的类型推导?比如下面的场景:

// 这个函数可能是未知的,或者存在重载
const foo = <T extends boolean>(args: T, cb: (v: T extends true ? 1 : 2) => void) => {}

const bar = <T extends Parameters<typeof foo>[0]>(
    args: T,
    // 这里的写法不生效,只是示例
    // 期望能根据第一个参数T,获取foo对应情况下的回调参数类型
    cb: Parameters<(typeof foo)[T]>[0]
) => {}

bar(true, (v /* 期望类型为1 */) => void 0)

核心需求是:如何根据第一个参数的泛型值T,动态获取泛型函数foo对应的回调参数类型?


解决方案:用条件类型提取对应泛型下的参数类型

我们可以定义一个工具类型,专门用来提取泛型函数在指定第一个参数类型T时,第二个参数的类型。具体实现如下:

// 工具类型:获取泛型函数F在第一个参数为T时的第二个参数类型
type GetSecondParam<F, T> = F extends <U extends boolean>(args: U, cb: infer C) => void 
  ? (U extends T ? C : never)
  : never;

// 原泛型函数
const foo = <T extends boolean>(args: T, cb: (v: T extends true ? 1 : 2) => void) => {}

// 实现bar函数
const bar = <T extends boolean>(
    args: T,
    cb: GetSecondParam<typeof foo, T>
) => {}

// 测试:v的类型会正确推导为1
bar(true, (v) => {
  console.log(v); // v: 1
});

// 测试:v的类型会正确推导为2
bar(false, (v) => {
  console.log(v); // v: 2
});

原理说明

  1. 工具类型GetSecondParam:
    • 先判断F是否符合<U extends boolean>(args: U, cb: infer C) => void的结构,通过infer C提取出回调参数的类型C
    • 再通过条件类型U extends T ? C : never,筛选出当第一个参数类型匹配T时对应的回调类型C
  2. bar函数的泛型约束:直接将T约束为boolean(和foo的泛型约束保持一致),然后用GetSecondParam<typeof foo, T>获取对应类型的回调参数

如果原函数的泛型约束不是boolean,只需要调整工具类型里的泛型约束即可,比如如果是string | number,就把U extends boolean改成U extends string | number。


处理函数重载的情况

如果foo存在重载,我们可以调整工具类型,遍历所有重载签名,找到匹配的那个:

// 支持重载的工具类型
type GetSecondParamOverloaded<F, T> = 
  F extends {
    (...args: infer A1): void;
    (...args: infer A2): void;
    (...args: infer A3): void;
  } 
  ? (A1[0] extends T ? A1[1] : A2[0] extends T ? A2[1] : A3[0] extends T ? A3[1] : never)
  : never;

// 带重载的foo函数
function foo(args: true, cb: (v: 1) => void): void;
function foo(args: false, cb: (v: 2) => void): void;
function foo<T extends boolean>(args: T, cb: (v: T extends true ? 1 : 2) => void) {}

const bar = <T extends boolean>(
    args: T,
    cb: GetSecondParamOverloaded<typeof foo, T>
) => {}

bar(true, (v) => { v; // 类型为1 })
bar(false, (v) => { v; // 类型为2 })

这种方式可以覆盖大多数重载场景,如果重载更多,可以继续扩展工具类型里的A4、A5等。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 22:05:56