如何用fp-ts实现TaskEither错误上报并注入默认值?
问题描述
现有一个返回TaskEither<Error, number>类型的异步函数:
import * as TE from 'fp-ts/TaskEither' const someAsyncFunction = TE.fromPredicate( (value: number) => value < 12, (value: number) => new Error("value was greater than 12") )
需求是:当函数返回错误时,将错误上报(示例中用console.log模拟Sentry上报),同时注入默认值,保证下游逻辑可以继续执行。
尝试编写的代码如下,但类型检查失败:
import * as E from 'fp-ts/Either' import * as TE from 'fp-ts/TaskEither' import { flow } from 'fp-ts/function' const reportError = (error: Error) => console.log(error.message) type ExceptionHandler = typeof reportError const unwrapAndReportError = <R>(captureFunction: ExceptionHandler) => ( def: R ) => (inputEither: TE.TaskEither<Error, R>): R => E.getOrElse((err: Error) => { captureFunction(err) return def })(inputEither) const runAsyncFunctionThenUnwrapAndReport = flow( someAsyncFunction, unwrapAndReportError(reportError)("potato") )
报错信息:
Argument of type 'TaskEither<Error, R>' is not assignable to parameter of type 'Either<Error, R>'
解决方案
错误原因
代码误用了E.getOrElse(Either类型的同步操作符),但传入的参数是TaskEither<Error, R>——它本质是Task<Either<Error, R>>(即包裹了Promise的Either),并非直接的Either类型,因此类型不匹配。
正确实现方式
处理TaskEither需使用其内置的异步操作符,推荐两种模式:
模式1:mapLeft上报错误 + getOrElse注入默认值
通过TE.mapLeft在错误分支完成上报,再用TE.getOrElse设置默认值,最终返回Task<R>(即Promise<R>):
import * as TE from 'fp-ts/TaskEither' import { flow } from 'fp-ts/function' const someAsyncFunction = TE.fromPredicate( (value: number) => value < 12, (value: number) => new Error(`value was greater than 12: ${value}`) ) const reportError = (error: Error) => { console.log(error.message) // 实际项目中替换为Sentry上报逻辑,如 Sentry.captureException(error) } type ExceptionHandler = typeof reportError const unwrapAndReportError = <R>(captureFunction: ExceptionHandler) => (def: R) => flow( // 错误分支:上报异常,同时透传错误 TE.mapLeft((err) => { captureFunction(err) return err }), // 注入默认值:错误时返回默认值的Promise,成功时返回原值 TE.getOrElse(() => Promise.resolve(def)) ) const runAsyncFunctionThenUnwrapAndReport = flow( someAsyncFunction, unwrapAndReportError(reportError)("potato") ) // 调用示例 runAsyncFunctionThenUnwrapAndReport(15).then(console.log) // 输出"potato",同时打印错误信息 runAsyncFunctionThenUnwrapAndReport(10).then(console.log) // 输出10
模式2:用fold合并错误与成功分支
TE.fold可以直接定义错误分支和成功分支的处理逻辑,代码更紧凑:
const unwrapAndReportError = <R>(captureFunction: ExceptionHandler) => (def: R) => TE.fold( // 错误分支:上报异常后返回默认值的Promise (err) => { captureFunction(err) return Promise.resolve(def) }, // 成功分支:直接返回原值的Promise (value) => Promise.resolve(value) )
核心原则
在fp-ts中处理TaskEither时,优先使用fp-ts/TaskEither模块提供的操作符,不要混用Either的同步操作符——因为TaskEither是异步容器,所有操作都需要适配异步语义。
内容的提问来源于stack exchange,提问作者Abraham P
相关产品推荐
相关产品推荐

