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

前端开发中除网络I/O外,何时使用fp-ts的Either?

在前端开发中使用fp-ts Either的非网络I/O场景

Either的核心价值是显式区分成功与失败分支,用类型系统约束错误处理,避免隐式的异常或空值问题。除了网络请求,前端还有很多场景适合用它:

1. 数据解析与转换

前端经常需要处理非可靠的字符串数据,解析失败是常见情况,用Either可以把错误捕获并明确标记:

  • JSON解析:处理后端非标准JSON、本地存储的JSON字符串时,替代try/catch让错误处理更优雅:
import { Either, tryCatch } from 'fp-ts/Either';

const parseJSON = <T>(str: string): Either<Error, T> =>
  tryCatch(
    () => JSON.parse(str),
    (err) => err as Error
  );
  • 格式转换:比如把字符串转数字、日期,转换失败时返回特定错误:
import { Either, left, right } from 'fp-ts/Either';

const toNumber = (str: string): Either<'invalid_number', number> => {
  const num = Number(str);
  return isNaN(num) ? left('invalid_number') : right(num);
};

2. 表单验证

复杂表单的多字段校验场景,用Either可以清晰返回字段级错误,结合fp-ts的sequenceT或Validation还能实现并行校验:

import { Either, left, right } from 'fp-ts/Either';

type FormError = { field: string; message: string };

const validateEmail = (email: string): Either<FormError, string> =>
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) 
    ? right(email) 
    : left({ field: 'email', message: '邮箱格式不符合要求' });

const validatePassword = (pwd: string): Either<FormError, string> =>
  pwd.length >= 6 && /[A-Z]/.test(pwd)
    ? right(pwd)
    : left({ field: 'password', message: '密码需至少6位且包含大写字母' });

3. 本地存储操作

LocalStorage/SessionStorage的读写存在多种失败可能:数据不存在、格式错误、存储空间不足,用Either统一处理这些异常:

import { Either, left, right } from 'fp-ts/Either';
import { parseJSON } from './utils'; // 复用之前的JSON解析函数

type UserPrefs = { theme: 'light' | 'dark'; notifications: boolean };

const getUserPrefs = (): Either<string, UserPrefs> => {
  const raw = localStorage.getItem('userPrefs');
  if (!raw) return left('未找到用户偏好设置');
  
  const parsed = parseJSON<UserPrefs>(raw);
  return parsed.fold(
    () => left('用户偏好设置格式损坏'),
    right
  );
};

4. 业务状态计算

从多个状态片段推导业务结果时,任何一个环节不符合规则都需要返回错误,Either能让错误分支更明确:

import { Either, left, right } from 'fp-ts/Either';

type CartItem = { id: string; quantity: number; price: string };
type CartError = 'invalid_price' | 'insufficient_stock';

const calculateCartTotal = (items: CartItem[], stock: Record<string, number>): Either<CartError, number> => {
  let total = 0;
  for (const item of items) {
    const price = parseFloat(item.price);
    if (isNaN(price)) return left('invalid_price');
    if (stock[item.id] < item.quantity) return left('insufficient_stock');
    total += price * item.quantity;
  }
  return right(total);
};

5. 前端文件处理

用户上传文件的读取、转换操作(比如FileReader读文本/Base64)可能失败,结合TaskEither处理异步场景:

import { TaskEither, tryCatch as tryCatchTask } from 'fp-ts/TaskEither';

const readFileAsText = (file: File): TaskEither<Error, string> =>
  tryCatchTask(
    () => new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result as string);
      reader.onerror = () => reject(reader.error);
      reader.readAsText(file);
    }),
    (err) => err as Error
  );

总结来说,只要是存在明确成功/失败分支,且需要显式处理错误的场景,都可以用Either替代传统的try/catch、返回null/undefined或抛出异常的方式,让代码的类型更安全,错误逻辑更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 09:01:09