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

TypeScript错误处理最佳实践:值对象校验抛异常是否合理

现有实现的可优化点

  1. isValidName() 方法命名隐含仅需返回布尔值完成校验判断,但内部仍抛出异常,不符合调用方的直觉预期
  2. 值对象的核心设计原则是「一旦创建就必须合法」,你当前的实现把非空校验放在构造阶段、格式校验放在后续调用方法里,会导致系统中可能存在非法的UserName实例流转,违背值对象的设计初衷。

更优的错误处理方案

- 用Result/Either包装创建结果,完全避免抛出异常

把构造函数完全私有化,所有实例只能通过静态工厂方法创建,工厂方法不直接返回UserName实例,而是返回一个包含「成功实例/错误信息」的包装对象,调用方可以直接判断成功/失败,不需要强制包裹try/catch。

通用Result包装类示例:

class Result<T> {
  public readonly isSuccess: boolean;
  public readonly error?: string;
  public readonly data?: T;

  private constructor(isSuccess: boolean, data?: T, error?: string) {
    this.isSuccess = isSuccess;
    this.data = data;
    this.error = error;
  }

  static success<T>(data: T): Result<T> {
    return new Result(true, data);
  }

  static fail<T>(error: string): Result<T> {
    return new Result(false, null, error);
  }
}

改造后的UserName实现:

export class UserName {
  private readonly value: string;

  // 构造函数完全私有化,禁止外部直接new
  private constructor(value: string) {
    this.value = value;
  }

  // 所有校验逻辑都收拢到工厂方法,实现创建即合法
  static create(name: string): Result<UserName> {
    const trimmedName = name.trim();
    if (!trimmedName) {
      return Result.fail("username is required!");
    }
    if (!/^[a-zA-Z]+$/.test(trimmedName)) {
      return Result.fail("user name should contain only letter");
    }
    return Result.success(new UserName(trimmedName));
  }

  // 取值方法,拿到的一定是合法值
  public getValue(): string {
    return this.value;
  }
}

调用方用法:

const userNameResult = UserName.create("test123");
if (!userNameResult.isSuccess) {
  console.log(userNameResult.error); // 直接获取错误信息,无需try/catch
  return;
}
const validUserName = userNameResult.data; // 拿到的实例必然合法

- 通知模式批量返回错误

如果需要一次性返回所有校验失败原因,而非遇到第一个错误就终止,可以调整工厂方法的错误字段为数组,收集所有错误后再返回,适合表单校验这类需要同时提示所有问题的场景。

- 轻量场景用元组返回结果

不想额外定义Result类的话,可以直接用TS的元组返回结果,写法更简洁:

static create(name: string): [Error|null, UserName|null] {
  const trimmedName = name.trim();
  if (!trimmedName) {
    return [new UserNameError("username is required!"), null];
  }
  if (!/^[a-zA-Z]+$/.test(trimmedName)) {
    return [new UserNameError("user name should contain only letter"), null];
  }
  return [null, new UserName(trimmedName)];
}

调用方直接解构即可:

const [err, userName] = UserName.create("test");
if (err) {
  // 处理错误逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 14:24:03