TypeScript中返回回调处理结果的函数类型声明方法——mapResponse函数类型定义问题
我来帮你修正mapResponse函数的TypeScript类型声明,让它能准确匹配函数的实际行为。先分析下原类型存在的问题,再给出完整的正确实现:
原类型的核心问题
Mapper的泛型定义冗余且不准确:原代码里给Mapper加了<R>泛型,但我们希望Mapper的参数类型严格绑定caller返回值的解包类型,而不是让Mapper自主泛化,这会导致类型推断混乱。- 返回值类型未区分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; }; }
类型逻辑详解
- 类型参数设计:
Caller:约束为任意函数,保留原逻辑。UnpackedCallerReturn:提前提取caller返回值的解包类型,作为Mapper的参数类型,确保两者严格对齐。MapperResult:明确Mapper的返回类型,让TypeScript能精准推断组合函数的最终返回值。Mapper:约束为接收UnpackedCallerReturn并返回MapperResult的函数,避免参数类型不匹配。
- 返回值条件类型:
通过条件判断Caller是否返回Promise,动态决定组合函数的返回类型——如果是异步caller,就返回Promise<MapperResult>;如果是同步caller,就直接返回MapperResult,完全匹配函数的实际行为。 - 函数体的类型断言优化:
把原代码里模糊的强制转换替换成更清晰的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
相关产品推荐
相关产品推荐

