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

如何识别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提示

本质都是通过结构化的错误信息,让客户端能精准处理不同错误场景,同时保证类型安全。

五、类型安全的最佳实践总结
  1. 永远不要reject字符串:字符串无法提供类型信息,客户端处理时只能做松散的字符串匹配,容易出错
  2. 用自定义错误类封装业务错误:每个错误类对应明确的业务场景,保持单一职责
  3. 添加错误码枚举:统一管理错误类型,方便团队协作和维护
  4. 区分业务异常和系统异常:在catch中先判断自定义错误,再处理未知的系统错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:48:41