TypeScript错误处理最佳实践:值对象校验抛异常是否合理
现有实现的可优化点
isValidName()方法命名隐含仅需返回布尔值完成校验判断,但内部仍抛出异常,不符合调用方的直觉预期- 值对象的核心设计原则是「一旦创建就必须合法」,你当前的实现把非空校验放在构造阶段、格式校验放在后续调用方法里,会导致系统中可能存在非法的
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
相关产品推荐
相关产品推荐

