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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:22:43