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

如何收窄neverthrow的Result数组类型,安全访问Ok的value?

解决neverthrow Result数组过滤后TypeScript类型推断问题

问题描述

我有一个来自neverthrow库的Result类型数组,需求是:检查数组中是否存在Err类型项,若存在则退出当前函数;若不存在Err项,希望将数组解构为对应的Ok类型,从而安全访问其value属性。但TypeScript无法自动识别此时数组已无Err类型,访问value时会抛出“Property 'value' does not exist on type 'Err<boolean, Error>'”的错误。示例代码如下:

import { Err, ok, Result } from "neverthrow";

function resultIsError(
  result: Result<unknown, Error>
): result is Err<unknown, Error> {
  return result.isErr();
}

function doSomething() {
  const results = [
    ok(true) as Result<boolean, Error>,
    ok("foobar") as Result<string, Error>,
    ok({ foo: "bar" }) as Result<{ foo: string }, Error>
  ];

  const resultErrors = results.filter(resultIsError);

  if (resultErrors.length > 0) {
    return resultErrors;
  }

  const [someBool, someString, someObj] = results;

  const someBoolValue: boolean = someBool.value;
  const someStringValue: string = someString.value;
  const someObjValue: { foo: string } = someObj.value;
}

解决方案

TypeScript默认不会为filter后的数组自动缩小类型范围,因此需要通过类型守卫、官方工具函数或类型断言来解决,以下是几种简洁优雅的处理方式:

方案一:使用neverthrow官方partition函数(推荐)

neverthrow提供了partition工具函数,可一次性将Result数组拆分为Ok数组和Err数组,TypeScript能自动推断出两个数组的准确类型:

import { Result, ok, partition } from "neverthrow";

function doSomething() {
  const results = [
    ok(true) as Result<boolean, Error>,
    ok("foobar") as Result<string, Error>,
    ok({ foo: "bar" }) as Result<{ foo: string }, Error>
  ];

  // 拆分Ok和Err数组,类型自动推断
  const [okResults, errResults] = partition(results);

  if (errResults.length > 0) {
    return errResults;
  }

  // 此时okResults已被推断为纯Ok类型数组,可安全访问value
  const [someBool, someString, someObj] = okResults;

  const someBoolValue: boolean = someBool.value;
  const someStringValue: string = someString.value;
  const someObjValue: { foo: string } = someObj.value;
}

方案二:自定义数组级类型守卫

如果需要自定义检查逻辑,可以写一个数组级的类型守卫函数,断言数组中不存在Err类型:

import { Err, Ok, Result, ok } from "neverthrow";

// 单个Result的错误类型守卫
function isErr<T, E>(result: Result<T, E>): result is Err<T, E> {
  return result.isErr();
}

// 数组类型守卫:断言数组全为Ok类型
function allOk<T extends readonly Result<any, any>[]>(
  results: T
): results is { [K in keyof T]: Ok<T[K]["_type"], T[K]["_error"]> } {
  return !results.some(isErr);
}

function doSomething() {
  const results = [
    ok(true) as Result<boolean, Error>,
    ok("foobar") as Result<string, Error>,
    ok({ foo: "bar" }) as Result<{ foo: string }, Error>
  ];

  // 通过类型守卫检查数组是否全为Ok
  if (!allOk(results)) {
    const errors = results.filter(isErr);
    return errors;
  }

  // 此时TypeScript能识别results为纯Ok数组
  const [someBool, someString, someObj] = results;

  const someBoolValue: boolean = someBool.value;
  const someStringValue: string = someString.value;
  const someObjValue: { foo: string } = someObj.value;
}

方案三:直接类型断言(简洁但需确保逻辑可靠)

如果追求代码简洁,且能保证前面的错误检查逻辑完全覆盖Err情况,可以在解构时直接断言数组类型:

import { Err, Ok, Result, ok } from "neverthrow";

function resultIsError(result: Result<unknown, Error>): result is Err<unknown, Error> {
  return result.isErr();
}

function doSomething() {
  const results = [
    ok(true) as Result<boolean, Error>,
    ok("foobar") as Result<string, Error>,
    ok({ foo: "bar" }) as Result<{ foo: string }, Error>
  ];

  const resultErrors = results.filter(resultIsError);

  if (resultErrors.length > 0) {
    return resultErrors;
  }

  // 直接断言数组为纯Ok类型数组
  const [someBool, someString, someObj] = results as [
    Ok<boolean, Error>,
    Ok<string, Error>,
    Ok<{ foo: string }, Error>
  ];

  const someBoolValue: boolean = someBool.value;
  const someStringValue: string = someString.value;
  const someObjValue: { foo: string } = someObj.value;
}

方案推荐

  • 优先选择方案一:利用官方工具函数,类型推断准确,代码简洁且符合库的设计规范。
  • 方案二适合需要自定义检查逻辑的场景,类型安全有保障。
  • 方案三适合快速实现,但需确保错误检查逻辑的可靠性,避免类型断言带来的潜在风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 18:35:27