如何收窄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
相关产品推荐
相关产品推荐

