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

如何从泛型函数映射中获取指定函数的返回类型?

问题描述

我有一个包含多态泛型函数的字典,其中的函数接受相同参数但返回不同结果:

const dict = {
  one: {
    foo<A>(a: A) {
      return [a] as const
    }
  },
  two: {
    foo<A>(a: A) {
      return [a, a] as const
    }
  },
  three: {
    foo<A>(a: A) {
      return [a, a, a] as const
    }
  }
}

type MapType = typeof dict // 注:原代码中`type Map = typeof map`应为笔误,修正为`typeof dict`

我希望通过辅助工具函数获取该字典中函数的返回类型,于是写了如下代码:

const wrapper = <N extends keyof MapType, A>() => {
  const fn = (() => {}) as unknown as MapType[N]['foo']
  return null as unknown as ReturnType<typeof fn<A>>
}

但尝试获取指定函数的返回类型时(比如传入参数7,获取'two'对应的返回类型),得到的是所有函数返回类型的联合类型,而非预期的单一类型:

// 实际返回类型: readonly [7] | readonly [7, 7] | readonly [7, 7, 7]
// 预期返回类型: readonly [7, 7]
type Result = typeof wrapper<'two', 7>

问题在于即使传入单一的键值,泛型参数N仍被视为联合类型,导致返回所有可能的结果。另外需要说明的是,我无法访问dict对象本身,它来自第三方库。

解决方案

问题的核心在于,当使用MapType[N]['foo']时,TypeScript无法正确将泛型函数的具体实例与N的具体值绑定,而是将所有可能的foo函数视为联合类型处理。以下是两种可行的解决方式:

方案1:纯类型工具(推荐)

不需要编写值级别的wrapper函数,直接通过类型映射和条件类型提取目标返回类型:

type MapType = typeof dict;

type GetFooReturnType<N extends keyof MapType, A> = 
  MapType[N]['foo'] extends (a: A) => infer R ? R : never;

// 测试:获取'two'对应foo函数传入7的返回类型
type Result = GetFooReturnType<'two', 7>; // 结果:readonly [7, 7]

方案2:修正值级别的wrapper函数

如果必须保留wrapper函数的形式,需要拆分泛型参数,先固定N再传入A,确保TypeScript能正确识别对应N的具体foo函数:

type MapType = typeof dict;

const wrapper = <N extends keyof MapType>() => {
  return <A>() => {
    type TargetFn = MapType[N]['foo'];
    return null as unknown as TargetFn extends (a: A) => infer R ? R : never;
  };
};

// 测试
type Result = ReturnType<ReturnType<typeof wrapper<'two'>><'7'>>; // 结果:readonly [7, 7]

原理说明

  • 方案1直接通过条件类型infer提取指定foo函数在传入参数A时的返回类型,完全在类型层面处理,避免了值级别的类型混淆。
  • 方案2将泛型参数拆分两次传入,先锁定N对应的具体foo函数,再传入参数类型A,确保TypeScript不会将所有foo函数视为联合类型处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 03:33:30