文章目录

TypeScript 错误处理:try-catch 与自定义错误

发布于 2026-04-08 05:28:08 · 浏览 8 次 · 评论 0 条

TypeScript 错误处理:try-catch 与自定义错误

在 TypeScript 开发中,直接使用 try-catch 捕获 anyunknown 类型的错误往往导致代码难以维护。为了精准区分业务逻辑失败、网络异常或输入校验错误,我们需要构建一套基于自定义错误类型的处理机制。


1. 理解 try-catch 的基础陷阱

TypeScript 中,catch 块接收的默认错误类型是 unknown。这意味着如果不进行类型判断,直接访问 error.message 会导致编译报错。

编写 基础的错误捕获代码,观察类型限制:

try {
  // 模拟一个可能会抛出错误的函数
  JSON.parse('{ invalid json }');
} catch (error) {
  // 此时 error 的类型是 unknown
  console.log(error.message); 
  // 错误: 对象的类型为 "unknown"
}

使用 类型断言或类型守卫来消除错误:

try {
  JSON.parse('{ invalid json }');
} catch (error) {
  // 简单判断是否存在 message 属性
  if (error instanceof Error) {
    console.log(error.message);
  } else {
    console.log('未知错误');
  }
}

这种方式虽然能解决编译错误,但无法区分错误的具体业务含义


2. 构建自定义错误类体系

为了区分“数据库连接失败”、“用户未授权”或“参数不合法”,我们需要创建 继承自 Error 的自定义类。

定义 一个基础的自定义错误类 AppError,并添加 code 属性用于存储错误码:

class AppError extends Error {
  constructor(
    public message: string,
    public code: number
  ) {
    super(message);
    this.name = 'AppError';
    // 维护原型链,确保 instanceof 正常工作
    Object.setPrototypeOf(this, AppError.prototype);
  }
}

扩展 具体的业务错误类,例如 ValidationError(校验错误)和 NetworkError(网络错误):

// 校验错误:通常由客户端输入引起
class ValidationError extends AppError {
  constructor(message: string) {
    super(message, 400); // HTTP 400 状态码
    this.name = 'ValidationError';
  }
}

// 网络错误:通常由后端或网络连接引起
class NetworkError extends AppError {
  constructor(message: string) {
    super(message, 503); // HTTP 503 状态码
    this.name = 'NetworkError';
  }
}

3. 错误处理流程设计

在实际应用中,当错误发生时,系统需要根据错误类型执行不同的逻辑(如重试、提示用户或记录日志)。以下流程展示了从抛出错误到最终处理的决策路径。

graph TD A["开始: 执行业务逻辑"] --> B{"抛出异常?"} B -- "否" --> C["执行成功"] B -- "是" --> D["进入 Catch 块"] D --> E{"错误类型判断"} E -- "ValidationError" --> F["提取 code: 400"] E -- "NetworkError" --> G["提取 code: 503"] E -- "未知错误" --> H["降级为通用错误"] F --> I["提示用户修正输入"] G --> J["触发重试机制或提示服务不可用"] H --> K["记录严重错误日志"]

4. 实施类型守卫与错误处理

为了让 TypeScript 能够在 catch 块中自动收窄 错误类型,我们需要使用 instanceof 作为类型守卫。

模拟 一个可能抛出不同错误的业务函数:

function processUserData(data: any) {
  if (!data.name) {
    // 抛出校验错误
    throw new ValidationError('姓名字段不能为空');
  }

  if (!data.id) {
    // 模拟一个网络请求失败
    throw new NetworkError('无法获取用户 ID');
  }

  return `Processing ${data.name}`;
}
```

**编写** 能够精准处理上述错误的 `try-catch` 逻辑:

```typescript
try {
  const result = processUserData({ name: '' });
  console.log(result);
} catch (error) {
  // 使用 instanceof 进行类型守卫
  if (error instanceof ValidationError) {
    // TypeScript 此处知道 error 是 ValidationError 类型
    console.error(`客户端错误 (${error.code}): ${error.message}`);
    // 执行 UI 提示操作
  } else if (error instanceof NetworkError) {
    // TypeScript 此处知道 error 是 NetworkError 类型
    console.error(`服务端错误 (${error.code}): ${error.message}`);
    // 执行重试逻辑
  } else if (error instanceof Error) {
    // 处理其他标准错误
    console.error(`系统意外错误: ${error.message}`);
  } else {
    // 处理非 Error 对象抛出的异常(如 throw 'string')
    console.error('未知异常类型', error);
  }
}

5. 异步函数中的错误处理

async/await 语法中,错误同样会被 catch 捕获。确保 所有的 Promise 都被正确包裹。

编写 异步业务函数:

async function fetchUserConfig() {
  // 模拟异步操作
  await new Promise(resolve => setTimeout(resolve, 100));
  throw new NetworkError('配置服务连接超时');
}

调用 异步函数并处理错误:

async function main() {
  try {
    await fetchUserConfig();
  } catch (error) {
    if (error instanceof NetworkError) {
      console.warn('配置加载失败,使用本地缓存');
    } else {
      console.error('无法恢复的错误');
    }
  }
}

6. 不同错误类型的策略对比

在系统设计中,不同类型的错误应对应不同的处理策略。下表列出了常见错误类型的处理建议。

错误类型 推荐处理动作 是否需要用户干预
ValidationError 修正 输入数据并重试
NetworkError 等待 短暂延迟后重试
AuthenticationError 跳转 至登录页面
CriticalSystemError 记录 日志并上报监控

注意:不要在 catch 块中吞掉错误(即留空 catch {}),除非你明确知道该错误是可以被安全忽略的。

执行 最终的错误处理代码逻辑:

class AuthenticationError extends AppError {
  constructor() {
    super('Token expired', 401);
    this.name = 'AuthenticationError';
  }
}

async function secureRequest() {
  try {
    // 业务逻辑...
    throw new AuthenticationError();
  } catch (error) {
    if (error instanceof AuthenticationError) {
      // 清除本地 Token
      localStorage.removeItem('token');
      // 重定向
      window.location.href = '/login';
    } else {
      // 对于非认证错误,继续向上抛出或处理
      throw error;
    }
  }
}

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文