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

TypeScript中返回回调处理结果的函数类型声明方法——mapResponse函数类型定义问题

我来帮你修正mapResponse函数的TypeScript类型声明,让它能准确匹配函数的实际行为。先分析下原类型存在的问题,再给出完整的正确实现:

原类型的核心问题

  1. Mapper的泛型定义冗余且不准确:原代码里给Mapper加了<R>泛型,但我们希望Mapper的参数类型严格绑定caller返回值的解包类型,而不是让Mapper自主泛化,这会导致类型推断混乱。
  2. 返回值类型未区分Promise场景:如果caller返回Promise,组合后的函数实际返回的是Promise<ReturnType<Mapper>>,但原类型标注成了ReturnType<Mapper>,会造成类型不匹配。

修正后的完整实现

export type Unpacked<T> = 
  T extends (infer U)[] ? U : 
  T extends (...args: any[]) => infer U ? U : 
  T extends Promise<infer U> ? U : 
  T;

export function mapResponse<
  Caller extends (...args: any[]) => unknown,
  UnpackedCallerReturn = Unpacked<ReturnType<Caller>>,
  MapperResult,
  Mapper extends (response: UnpackedCallerReturn) => MapperResult
>(
  caller: Caller,
  mapper: Mapper
): Caller extends (...args: any[]) => Promise<any> 
  ? (...args: Parameters<Caller>) => Promise<MapperResult> 
  : (...args: Parameters<Caller>) => MapperResult {
  return (...args: Parameters<Caller>) => {
    const res = caller(...args);
    if (res instanceof Promise) {
      return res.then(mapper) as Promise<MapperResult>;
    }
    return mapper(res as UnpackedCallerReturn) as MapperResult;
  };
}

类型逻辑详解

  1. 类型参数设计:
    • Caller:约束为任意函数,保留原逻辑。
    • UnpackedCallerReturn:提前提取caller返回值的解包类型,作为Mapper的参数类型,确保两者严格对齐。
    • MapperResult:明确Mapper的返回类型,让TypeScript能精准推断组合函数的最终返回值。
    • Mapper:约束为接收UnpackedCallerReturn并返回MapperResult的函数,避免参数类型不匹配。
  2. 返回值条件类型:
    通过条件判断Caller是否返回Promise,动态决定组合函数的返回类型——如果是异步caller,就返回Promise<MapperResult>;如果是同步caller,就直接返回MapperResult,完全匹配函数的实际行为。
  3. 函数体的类型断言优化:
    把原代码里模糊的强制转换替换成更清晰的as UnpackedCallerReturn和as Promise<MapperResult>,确保类型断言的准确性。

验证示例

// 同步caller场景
const syncGetUser = (id: number) => ({ id, username: "maksym" });
const getUsername = mapResponse(syncGetUser, (user) => user.username);
// getUsername类型:(id: number) => string
console.log(getUsername(1)); // 输出 "maksym"

// 异步caller场景
const asyncGetUser = (id: number) => Promise.resolve({ id, username: "async-maksym" });
const getAsyncUsername = mapResponse(asyncGetUser, (user) => user.username);
// getAsyncUsername类型:(id: number) => Promise<string>
getAsyncUsername(2).then(name => console.log(name)); // 输出 "async-maksym"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 17:14:06