学习TypeScript中捕捉子句中的狭义类型

1,566 阅读3分钟

当你来自Java、C++或C#等语言时,你习惯于通过抛出异常来进行错误处理。随后,在一连串的catch 子句中捕捉它们。可以说,有更好的方法来做错误处理,但这个方法已经存在了很久,考虑到历史和影响,它也已经进入了JavaScript。

所以,这是在JavaScript和TypeScript中做错误处理的有效方法。但是,尽量遵循与其他编程语言相同的流程,并在你的catch 子句中注释错误。

try {
  // something with Axios, for example
} catch(e: AxiosError) {
//         ^^^^^^^^^^ Error 1196 💥
}

TypeScript会出现TS1196的错误。Catch子句的变量类型注释必须是'any'或'unknown',如果指定的话。

这有几个原因。

1.任何类型都可以被抛出#

在JavaScript中,你被允许抛出每一个表达式。当然,你可以抛出 "异常"(或错误,我们在JavaScript中称之为错误),但也可以抛出任何其他值。

throw "What a weird error"; // 👍
throw 404; // 👍
throw new Error("What a weird error"); // 👍

因为任何有效的值都可以被抛出,所以可能被捕捉的值已经比你通常的子类型Error 更加广泛。

2.在JavaScript中只有一个catch子句#

JavaScript每个try 语句只有一个catch 子句。在遥远的过去,曾经有人提出过多个catch子句,甚至是条件表达式,但它们从未体现出来。

相反,你应该使用这一个catch 子句,并做instanceoftypeof 检查(Source]。

try {
  myroutine(); // There's a couple of errors thrown here
} catch (e) {
  if (e instanceof TypeError) {
    // A TypeError
  } else if (e instanceof RangeError) {
    // Handle the RangeError
  } else if (e instanceof EvalError) {
    // you guessed it: EvalError
  } else if (typeof e === "string") {
    // The error is a string
  } else if (axios.isAxiosError(e)) {
    // axios does an error check for us!
  } else {
    // everything else  
    logMyErrors(e);
  }
}

注意:上面的例子也是TypeScript中为catch 子句缩小类型的唯一正确方法。

而且,由于所有可能的值都可以被抛出,而我们每个try 语句只有一个catch 子句来处理它们,所以e 的类型范围异常广泛。

3.任何异常都可能发生#

但是,既然你知道每一个可能发生的错误,那么一个适当的联合类型与所有可能的 "可抛物 "不是一样好用吗?在理论上,是的。在实践中,我们没有办法知道异常会有哪些类型。

在你所有的用户定义的异常和错误旁边,系统可能会在遇到类型不匹配或你的一个函数一直未定义的情况下,在内存出现问题时抛出错误。一个简单的函数调用可能会超过你的调用栈,导致臭名昭著的栈溢出

广泛的可能值,单一的catch 子句,以及发生错误的不确定性只允许e 的两种可能类型:anyunknown

那么Promise拒绝呢?#

如果你拒绝一个Promise,情况也是如此。TypeScript允许你指定的唯一东西是已完成的Promise的类型。拒绝可以以你的名义发生,也可以通过系统错误发生。

const somePromise = () => new Promise((fulfil, reject) => {
  if (someConditionIsValid()) {
    fulfil(42);
  } else {
    reject("Oh no!");
  }
});

somePromise()
  .then(val => console.log(val)) // val is number
  .catch(e => {
    console.log(e) // e can be anything, really.
  })

如果你在asnyc/await 流程中调用同一个承诺,就会变得更清楚。

try {
  const z = await somePromise(); // z is number
} catch(e) {
  // same thing, e can be anything!
}

底线#

如果你来自其他具有类似功能的编程语言,JavaScript和TypeScript的错误处理可能是一个 "假朋友"。要意识到这些差异,相信TypeScript团队和类型检查器会给你正确的控制流,以确保你的错误处理得足够好。