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

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);
  }
});

泛型知识点解析

  1. 条件类型:IsPromise<T> extends true ? never : Result<T>用于限制同步函数不能返回Promise,避免类型歧义。
  2. 类型推断(infer):T extends Promise<infer V> ? V : T从Promise类型中提取内部值的类型V,实现Promise的“展开”。
  3. 函数重载:通过多个重载签名,让TypeScript根据传入函数的返回值类型,自动匹配对应的返回类型,解决泛型推断的歧义问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:52:39