异常处理

JavaScript 有一个 Error 类,用于处理异常。你可以通过 throw 关键字来抛出一个错误。然后通过 try/catch 块来捕获此错误:

  1. try {
  2. throw new Error('Something bad happened');
  3. } catch (e) {
  4. console.log(e);
  5. }

错误子类型

除内置的 Error 类外,还有一些额外的内置错误,它们继承自 Error 类:

RangeError

当数字类型变量或者参数超出其有效范围时,出现 RangeError 的错误提示:

  1. // 使用过多参数调用 console
  2. console.log.apply(console, new Array(1000000000)); // RangeError: 数组长度无效

ReferenceError

当引用无效时,会出现 ReferenceError 的错误提示:

  1. 'use strict';
  2. console.log(notValidVar); // ReferenceError: notValidVar 未定义

SyntaxError

当解析无效 JavaScript 代码时,会出现 SyntaxError 的错误提示:

  1. 1 *** 3 // SyntaxError: 无效的标记 *

TypeError

变量或者参数不是有效类型时,会出现 TypeError 的错误提示:

  1. '1.2'.toPrecision(1); // TypeError: '1.2'.toPrecision 不是函数。

URIError

当传入无效参数至 encodeURI()decodeURI() 时,会出现 URIError 的错误提示:

  1. decodeURI('%'); // URIError: URL 异常

使用 Error

JavaScript 初学者可能有时候仅仅是抛出一个原始字符串:

  1. try {
  2. throw 'Something bad happened';
  3. } catch (e) {
  4. console.log(e);
  5. }

不要这么做,使用 Error 对象的基本好处是,它能自动跟踪堆栈的属性构建以及生成位置。

原始字符串会导致极差的调试体验,并且在分析日志时,将会变得错综复杂。

你并不需要 throw 抛出一个错误

传递一个 Error 对象是没问题的,这种方式在 Node.js 回调函数中非常常见,它用第一个参数作为错误对象进行回调处理。

  1. function myFunction (callback: (e: Error)) {
  2. doSomethingAsync(function () {
  3. if (somethingWrong) {
  4. callback(new Error('This is my error'));
  5. } else {
  6. callback();
  7. }
  8. })
  9. }

优秀的用例

「Exceptions should be exceptional」是计算机科学中常用用语。这里有一些原因说明在 JavaScript(TypeScript) 中也是如此。

不清楚从哪里抛出错误

考虑如下代码块:

  1. try {
  2. const foo = runTask1();
  3. const bar = runTask2();
  4. } catch (e) {
  5. console.log('Error:', e);
  6. }

下一个开发者可能并不清楚哪个函数可能会抛出错误。在没有阅读 task1/task2 代码以及他们可能会调用的函数时,对代码 review 的人员可能也不会知道错误会从哪里抛出。

优雅的捕获错误

你可以通过为每个可能抛出错误的代码显式捕获,来使其优雅:

  1. try {
  2. const foo = runTask1();
  3. } catch (e) {
  4. console.log('Error:', e);
  5. }
  6. try {
  7. const bar = runTask2();
  8. } catch (e) {
  9. console.log('Error:', e);
  10. }

但是现在,如果你想从第一个任务中传递变量到第二个任务中,代码会变的混乱(注意:foo 变量需要用 let 显式注解它,因为它不能从 runTask1 中返回出来):

  1. let foo: number; // Notice 使用 let 并且显式注名类型注解
  2. try {
  3. foo = runTask1();
  4. } catch (e) {
  5. console.log('Error:', e);
  6. }
  7. try {
  8. const bar = runTask2();
  9. } catch (e) {
  10. console.log('Error:', e);
  11. }

没有在类型系统中很好的表示

考虑如下函数:

  1. function validate(value: number) {
  2. if (value < 0 || value > 100) {
  3. throw new Error('Invalid value');
  4. }
  5. }

在这种情境下使用 Error 不是一个好的主意。因为没有用来验证函数的类型定义(如:(value: number) => void),取而代之一个更好的方式是创建一个验证方法:

  1. function validate(
  2. value: number;
  3. ): {
  4. error?: string;
  5. } {
  6. if (value < 0 || value > 100) {
  7. return { error: 'Invalid value' };
  8. }
  9. }

现在它具有类型定义了。

TIP

除非你想用以非常通用(try/catch)的方式处理错误,否则不要抛出错误。

原文: https://jkchao.github.io/typescript-book-chinese/typings/exceptionsHanding.html