在 TypeScript 的 catch 代码块中获取错误信息
最近在试着用 TypeScript 写点东西,在用 catch (error) {}
代码块处理异常的时候,看到了一个很难理解的错误 TS18046: error is of type unknown
。网上一顿冲浪之后,看到了 Kent C. Dodds 的一篇博客 Get a catch block error message with TypeScript。我跟着文章的内容成功解决了这个问题,并且解答了我的疑惑,所以想要翻译出来帮助到更多的人。
以下内容除特别注明外,皆翻译自原文。我亦不对内容做任何的担保,并不对任何可能产生的后果(包括但不限于文件丢失或功能异常)负责。
好吧,咱们看看这个代码:
1 | const reportError = ({ message }) => { |
这么写应该足够好了吧?嘛,毕竟这是 JavaScript。要是换成 TypeScript 的话:
1 | const reportError = ({ message }: { message: string }) => { |
这时候 reportError
的 error.message
这部分就要报错了。因为(就在最近)TypeScript 把 error
的类型定义成了 unknown
。这倒也是事实,因为它确实没法保证抛出的错误的类型。哦对,这也是你不能用 promise 的泛型 (Promise<ResolvedValue, NopeYouCantProvideARejectedValueType>
),来给 promise reject 的.catch(error => {})
指定类型的原因。而且,被抛出来的东西可能都不是一个 error,它可以是任何东西:
1 | throw '啥玩意?!' // 译者注:原文为 'What the!?',请尝试用东北口音理解 |
说真的,你可以想 throw 啥就 throw 啥,啥东西都行。那,要解决上面提到的错误信息好像挺简单对吧,我们在 catch
中声明代码只会抛出 error 不就行了?
1 | try { |
想的美!现在你会得到这么一条 TypeScript 的编译错误:
1 | Catch clause variable type annotation must be 'any' or 'unknown' if specified. ts(1196) |
报这个错的原因是,尽管看起来在我们的代码里不可能会抛出来其他的东西,但 JavaScript 就这么逗,一个第三方库完全有可能做点什么奇怪的事,比如给 Error 的构造函数来个猴子补丁(译者注:monkey-patching),让它抛出点不一样的东西:
1 | Error = function () { |
那咱们开发者该怎么办?我们只能尽力,比如这样:
1 | try { |
妥了这不!现在 TypeScript 也不跟我们嚷嚷有问题了,而且万一这个 error 是什么奇怪的东西,我们也用了合适的办法来处理它了。而且这段代码我们还能继续优化成这样:
1 | try { |
那么现在,如果这个 error 不是一个 Error
对象,那么我们就直接把它变成一个字符串并祈祷这个错误信息能有点用。
然后,我们还能把这段代码抽出来做成一个工具方法来给所有的 catch 块用:
1 | function getErrorMessage(error: unknown) { |
这个写法在我的项目里面特别好用。希望也能帮助到你!
更新:Nicolas针对 error 对象并不真的是 error 的情况提了一个很好的建议。此外 Jesse也提出了一个建议,在可能的情况下把 error 对象也转换成字符串。把这些结合起来,我们就得到了这样的一份代码:
1 | type ErrorWithMessage = { |
简直太好使了!
结论
我觉得关键在于,尽管 TypeScript 有一些奇怪的地方,但也绝对不要因为你觉得这不可能就忽略 TypeScript 抛出的编译错误或警告。大多数情况下,意外是非常有可能发生的,而 TypeScript 很好的强制你去处理这些 “不太可能发生” 的情况…… 然后你也很有可能会发现,这些情况并没有你想的那么少见。
译者的碎碎念:作为一个 TypeScript 纯新手的我,最后这段代码给我看傻了。Java 里面非常常见的 try {...} catch (Exception e) {...}
在 TypeScript 里面竟然能玩这么花……