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

如何在TypeScript中实现类型安全的Result数组unwrap函数?

实现符合TypeScript类型规则的unwrap函数

先定义基础类型,与你给出的结构一致:

type Result<T> = { status: 'success', data: T } | { status: 'failure' };

type ResultArray<T extends unknown[]> = readonly [...{
    [K in keyof T]: Result<T[K]>
}];

方案一:用Array.reduce实现(带精确类型)

可以给reduce的累加器指定明确类型,同时在逻辑中处理失败分支:

function unwrap<T extends unknown[]>(arr: ResultArray<T>): Result<T> {
  return arr.reduce<Result<T[]>>(
    (acc, curr) => {
      // 已有失败结果,直接返回
      if (acc.status === 'failure') return acc;
      // 当前元素为失败,返回失败状态
      if (curr.status === 'failure') return { status: 'failure' };
      // 成功则合并数据
      return {
        status: 'success',
        data: [...acc.data, curr.data]
      };
    },
    // 初始值:空数组对应的成功状态
    { status: 'success', data: [] }
  ) as Result<T>;
}

类型说明

  • 给reduce指定泛型Result<T[]>,明确累加器的类型范围;
  • 最后用as Result<T>做类型断言——TypeScript无法自动推导数组T[]与元组T的等价关系,但我们可以确定逻辑上最终的data就是目标元组的实例。

方案二:递归实现(解决空数组基础情况)

如果你倾向于递归方式,可以调整递归逻辑,明确空数组的处理分支:

// 递归推导ResultArray的返回类型
type UnwrapResultArray<T extends readonly Result<unknown>[]> = 
  T extends [] 
    ? Result<[]>
    : T extends [Result<infer First>, ...infer Rest extends readonly Result<unknown>[]]
      ? UnwrapResultArray<Rest> extends Result<infer RestData>
        ? RestData extends never // 剩余数组存在失败,整体返回失败
          ? Result<never>
          : First extends Result<infer F>
            ? F extends never // 当前元素是失败,整体返回失败
              ? Result<never>
              : Result<[F, ...RestData]>
            : never
        : never
      : never;

// 函数声明与实现
function unwrapRecursive<T extends readonly Result<unknown>[]>(arr: T): UnwrapResultArray<T> {
  if (arr.length === 0) {
    return { status: 'success', data: [] } as UnwrapResultArray<T>;
  }
  const first = arr[0];
  if (first.status === 'failure') {
    return { status: 'failure' } as UnwrapResultArray<T>;
  }
  const restResult = unwrapRecursive(arr.slice(1) as any);
  if (restResult.status === 'failure') {
    return restResult as UnwrapResultArray<T>;
  }
  return {
    status: 'success',
    data: [first.data, ...restResult.data]
  } as UnwrapResultArray<T>;
}

递归类型说明

  • 基础情况:当输入为空数组[]时,返回Result<[]>(成功状态+空数据数组);
  • 递归拆分:将数组拆分为第一个元素与剩余元素,只要其中任意一个是失败状态,整体结果就为失败;若全为成功,则合并数据类型为对应元组。

测试验证

两种实现都能满足你的示例需求:

全成功场景

unwrap([
  { status: 'success', data: 1 },
  { status: 'success', data: 'a'},
  { status: 'success', data: true}
]);
// 返回值:{ status: 'success', data: [1, 'a', true] }
// 返回类型:Result<[number, string, boolean]>

包含失败场景

unwrap([
  { status: 'failure' },
  { status: 'success', data: 'a'},
  { status: 'success', data: true}
]);
// 返回值:{ status: 'failure' }
// 返回类型:Result<never>(等价于失败状态)

内容的提问来源于stack exchange,提问作者Anthony S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 14:45:31