TypeScript高阶函数回调返回Promise时的返回值类型推断问题
合并同步/异步错误处理泛型函数的解决方案
首先定义通用的Result类型,模拟Rust风格的结果返回:
type Result<T, E = Error> = | { ok: true; value: T } | { ok: false; error: E };
原有的两个函数实现
你之前分开的同步/异步错误处理函数逻辑是清晰的:
// 同步处理:捕获同步函数的异常,返回Result<T> function trySync<T>(fn: () => T): Result<T> { try { return { ok: true, value: fn() }; } catch (e) { return { ok: false, error: e instanceof Error ? e : new Error(String(e)) }; } } // 异步处理:捕获Promise的reject,返回Promise<Result<V>> async function tryAsync<V>(fn: () => Promise<V>): Promise<Result<V>> { try { const value = await fn(); return { ok: true, value }; } catch (e) { return { ok: false, error: e instanceof Error ? e : new Error(String(e)) }; } }
合并时的问题根源
直接合并成单一泛型函数时,TypeScript无法自动区分同步函数和返回Promise的异步函数,会把异步函数的返回值Promise<Response>直接作为Result的泛型参数,导致返回Result<Promise<Response>, Error>而非预期的Promise<Result<Response, Error>>。
解决方案:结合函数重载与条件类型
通过函数重载为不同输入场景指定返回类型,再用条件类型和类型推断处理Promise的展开:
// 辅助类型:判断类型是否为Promise type IsPromise<T> = T extends Promise<any> ? true : false; // 重载1:传入同步函数(返回非Promise类型),返回Result<T> function tryCatch<T>(fn: () => T): IsPromise<T> extends true ? never : Result<T>; // 重载2:传入异步函数(返回Promise),返回Promise<Result<V>> function tryCatch<V>(fn: () => Promise<V>): Promise<Result<V>>; // 核心实现 function tryCatch<T>(fn: () => T): Result<T> | Promise<Result<T extends Promise<infer V> ? V : T>> { try { const result = fn(); // 判断返回值是否为Promise,分别处理同步/异步逻辑 if (result instanceof Promise) { return result.then( value => ({ ok: true, value }), error => ({ ok: false, error: error instanceof Error ? error : new Error(String(error)) }) ) as Promise<Result<T extends Promise<infer V> ? V : T>>; } else { return { ok: true, value: result } as Result<T>; } } catch (e) { // 捕获同步执行时的异常 return { ok: false, error: e instanceof Error ? e : new Error(String(e)) } as Result<T>; } }
测试验证
// 同步调用:类型推断为Result<{ name: string }, Error> const syncParse = tryCatch(() => JSON.parse('{"name":"Saverio"}')); if (syncParse.ok) { console.log(syncParse.value.name); // 正常访问value } else { console.error(syncParse.error); } // 异步调用:类型推断为Promise<Result<Response, Error>> const asyncFetch = tryCatch(() => fetch('https://api.example.com')); asyncFetch.then(res => { if (res.ok) { console.log(res.value.status); // 正常访问Response对象 } else { console.error(res.error); } });
泛型知识点解析
- 条件类型:
IsPromise<T> extends true ? never : Result<T>用于限制同步函数不能返回Promise,避免类型歧义。 - 类型推断(infer):
T extends Promise<infer V> ? V : T从Promise类型中提取内部值的类型V,实现Promise的“展开”。 - 函数重载:通过多个重载签名,让TypeScript根据传入函数的返回值类型,自动匹配对应的返回类型,解决泛型推断的歧义问题。
内容的提问来源于stack exchange,提问作者Saverio
相关产品推荐
相关产品推荐

