如何识别JavaScript Promise中的多种错误?TypeScript登录场景疑问
这确实是TypeScript处理业务级错误时非常常见的困惑,我来一步步帮你梳理优化方案:
一、先解决自定义错误类繁琐的问题
你当前的错误类写法重复且冗余,我们可以通过抽象一个基础错误基类来复用代码,避免每个错误类都重复原型链修复逻辑:
// 定义错误码枚举,统一管理业务错误类型 export enum AuthErrorCode { UserNotFound = "USER_NOT_FOUND", UserNotConfirmed = "USER_NOT_CONFIRMED", PasswordIncorrect = "PASSWORD_INCORRECT", LoginFailed = "LOGIN_FAILED" } // 基础认证错误基类 export class BaseAuthError extends Error { public readonly code: AuthErrorCode; constructor(code: AuthErrorCode, message?: string) { super(message || code); // 统一修复原型链(仅需在基类中写一次) Object.setPrototypeOf(this, new.target.prototype); // 明确错误名称,方便调试 this.name = new.target.name; } } // 具体业务错误类,只需继承基类即可 export class UserNotFoundError extends BaseAuthError { constructor(message = "用户名不存在") { super(AuthErrorCode.UserNotFound, message); } } export class UserNotConfirmedError extends BaseAuthError { constructor(message = "用户未完成邮箱验证") { super(AuthErrorCode.UserNotConfirmed, message); } } export class PasswordIncorrectError extends BaseAuthError { constructor(message = "密码错误") { super(AuthErrorCode.PasswordIncorrect, message); } } export class LoginError extends BaseAuthError { constructor(message = "登录失败,请稍后重试") { super(AuthErrorCode.LoginFailed, message); } }
这样每个具体错误类只需要关注自身的业务含义,无需重复原型链处理逻辑,代码简洁很多。
二、catch中错误识别的正确姿势
你觉得用instanceof奇怪其实是多虑了——这是TypeScript中类型最安全的错误判断方式,因为它能让编译器准确推断错误类型,避免字符串匹配的松散性。不过我们可以结合错误码枚举进一步优化代码结构:
private login() { this.auth.login({ email: this.email.value, password: this.password.value }) .then(() => { this.navCtrl.setRoot(HomePage); }) .catch((err: Error) => { // 先判断是否是我们定义的业务错误 if (err instanceof BaseAuthError) { switch (err.code) { case AuthErrorCode.UserNotFound: this.email.setErrors({ usernotfound: true }); break; case AuthErrorCode.UserNotConfirmed: this.form.setErrors({ usernotconfirmed: true }); break; case AuthErrorCode.PasswordIncorrect: this.password.setErrors({ passwordincorrect: true }); break; default: this.form.setErrors({ ooops: true }); } } else { // 处理系统级错误(比如网络异常、未知错误) this.form.setErrors({ ooops: true }); } }) }
这种方式既保留了类型安全,又比一串instanceof判断更整洁,同时错误码枚举也方便团队统一维护错误类型。
三、要不要在TypeScript中避免使用异常?
完全不需要——Promise的reject机制本身就是用来处理异常场景的,而自定义业务错误类是区分「业务异常」和「系统异常」的最佳方式:
- 业务异常:比如用户名不存在、密码错误,属于可预见的业务场景,需要明确反馈给用户
- 系统异常:比如网络超时、服务端崩溃,属于不可预见的技术问题
用自定义错误类封装业务异常,能让客户端代码清晰区分两类错误,做出不同的处理逻辑,这比直接抛出字符串要可靠得多。
四、主流库/框架的处理方式
大部分成熟的库和框架都会采用「自定义错误类+错误码」的模式:
- Axios:在错误对象中包含
response.status和error.code来区分HTTP错误类型 - NestJS:允许开发者自定义业务异常类,结合错误码和消息返回标准化的错误响应
- Redux Toolkit Query:在错误处理中支持通过自定义错误类型判断,返回对应UI提示
本质都是通过结构化的错误信息,让客户端能精准处理不同错误场景,同时保证类型安全。
五、类型安全的最佳实践总结
- 永远不要reject字符串:字符串无法提供类型信息,客户端处理时只能做松散的字符串匹配,容易出错
- 用自定义错误类封装业务错误:每个错误类对应明确的业务场景,保持单一职责
- 添加错误码枚举:统一管理错误类型,方便团队协作和维护
- 区分业务异常和系统异常:在catch中先判断自定义错误,再处理未知的系统错误
内容的提问来源于stack exchange,提问作者ovg
相关产品推荐
相关产品推荐

