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

TypeScript工厂函数返回函数包装器的泛型嵌套类型定义问题

解决TypeScript工厂函数泛型类型推导问题

问题根源

原代码中createSafeFactory的泛型U和F被定义为无约束的宽泛函数类型,无法关联后续被包装函数的返回值类型T和错误类型E,导致TypeScript无法推断出onOk(result)和onErr(error)的具体类型,最终退化为unknown。

修改后的实现代码

const mayThrow = (fail: boolean) => {
  if (fail) {
    throw new Error('failed')
  }
  return 'ok'
}

type Ok<T> = { ok: true, value: T }
type Err<E> = { ok: false, error: E }
type Result<T, E> = Ok<T> | Err<E>
const onOk = <T>(value: T): Ok<T> => ({ ok: true, value })
const onErr = <E>(error: E): Err<E> => ({ ok: false, error })

function createSafeFactory<
  U extends <T>(value: T) => unknown,
  F extends <E>(error: E) => unknown
>(onOk: U, onErr: F) {
  // 提取U在输入类型T时的返回类型
  type OkResult<T> = U extends <X>(x: X) => infer R ? R : never;
  // 提取F在输入类型E时的返回类型
  type ErrResult<E> = F extends <X>(x: X) => infer R ? R : never;

  return <A extends any[], T, E = unknown>(
    fn: (...args: A) => T,
    errorFn?: (error: unknown) => E
  ) => {
    return (...args: A): OkResult<T> | ErrResult<E> => {
      try {
        const result = fn(...args);
        // 类型断言帮助TypeScript关联泛型参数T
        return onOk(result) as OkResult<T>;
      } catch (e) {
        const error = errorFn ? errorFn(e) : (e as E);
        // 类型断言帮助TypeScript关联泛型参数E
        return onErr(error) as ErrResult<E>;
      }
    };
  };
}

// 测试
const safe = createSafeFactory(onOk, onErr)
// const safe: <A extends any[], T, E = unknown>(fn: (...args: A) => T, errorFn?: ((error: unknown) => E) | undefined) => (...args: A) => Ok<T> | Err<E>

const safeFn = safe(mayThrow)
// const safeFn: (fail: boolean) => Ok<string> | Err<unknown>

const res = safeFn(true)
// const res: Ok<string> | Err<unknown>

if (res.ok) {
  const data = res.value
  // const data: string  // 现在类型正确推导为string
}

// 带errorFn的测试
const safeFnWithCustomError = safe(mayThrow, e => (e as Error).message)
// const safeFnWithCustomError: (fail: boolean) => Ok<string> | Err<string>

关键改动说明

  1. 泛型约束收紧:将U和F约束为泛型函数类型,确保它们能接收任意类型的输入,并返回与输入类型绑定的结果,为后续类型关联打下基础。
  2. 动态返回类型定义:通过OkResult<T>和ErrResult<E>两个条件类型,提取U和F在接收T/E类型输入时的具体返回类型。
  3. 类型断言辅助推断:由于TypeScript无法自动推断泛型函数的实例化类型,通过as OkResult<T>和as ErrResult<E>帮助编译器关联正确的类型参数。
  4. 错误类型默认值:给E设置默认值unknown,处理未传入errorFn时的错误类型推断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 07:20:56