简单了解TypeScript中如何继承 Error 类
前端  /  管理员 发布于 4年前   396
前言
在JavaScript 中很多时候都需要自定义错误,尤其是开发 Node.js 应用的时候。 比如一个典型的网站服务器可能需要有 NetworkError, DatabaseError, UnauthorizedError 等。 我们希望这些类都拥有 Error 的特性:有错误消息、有调用栈、有方便打印的 toString 等。 最直观的实现方式便是 继承 Error 类。 但考虑 TypeScript 需要编译到 ES5 兼容性问题会较为复杂, 本文用来帮助理解 TypeScript 中继承 Error 的问题来源以及对应的几种解决方式。
我们需要怎样的 CustomError
为了容易讨论最佳实践,首先明确我们自定义的 CustomError 需要做到哪些功能。 下面是 Harttle 的观点:
下面举个例子,这是一个 message 为 "intended" 的 CustomError 的 .stack 属性值:
CustomError: intendedat Object. (/Users/harttle/Downloads/bar/a.js:10:13)at Module._compile (module.js:653:30)at Object.Module._extensions..js (module.js:664:10)at Module.load (module.js:566:32)at tryModuleLoad (module.js:506:12)at Function.Module._load (module.js:498:3)at Function.Module.runMain (module.js:694:10)at startup (bootstrap_node.js:204:16)at bootstrap_node.js:625:3
ES5 中如何继承 Error?
Error 是一个特殊的对象,或者说 JavaScript 的 new 是一个奇葩的存在。 为方便后续讨论,我们先讨论组 ES5 时代是怎样继承 Error 的。 我们说 JavaScript 是一门混杂的语言,如何继承 Error 就是一个典型的例子。 如果你熟悉 原型继承的方式,应该会写出如下代码:
function CustomError (message) {Error.call(this, message)}CustomError.prototype = new Error()
因为 stack 只在 new 的时候生成,上述实现不能满足功能 2 和功能 3,也就是说:
Node 文档 中描述了一个 captureStackTrace 方法来解决这个问题,改动后的实现如下:
function CustomError (msg) {this.name = 'CustomError'this.message = msgError.captureStackTrace(this, CustomError)}CustomError.prototype = new Error()
其中 .captureStackTrace() 会使用传入对象的 name 和 message 来生成 stack 的前缀;同时第二个参数用来指定在调用栈中忽略掉哪一部分,这样栈就会指向 new CustomError 的地方而不是 captureStackTrace() 的地方。
ES6 中如何继承 Error?
既然 ES6 通过 class 和 extends 等关键字给出了类继承机制, 那么想必通过编写 CustomError 类来继承 Error。事实也确实如此,只需要在构造函数中调用父类构造函数并赋值 name 即可实现文章开始提到的三个功能:
class CustomError extends Error {constructor(msg) {super(msg)this.name = 'CustomError'}}
TypeScript 中如何继承 Error?
ES6 中提供了 new.target 属性, 使得 Error 的构造函数中可以获取 CustomError 的信息,以完成原型链的调整。 因此 TypeScript 需要编译到 ES5 时上述功能仍然是无法自动实现。 在 TypeScript 中的体现是形如上述 ES6 的代码片段会被编译成:
var CustomError = /** @class */ (function (_super) {__extends(CustomError, _super);function CustomError(msg) {var _this = _super.call(this, msg) || this;_this.name = 'CustomError';return _this;}return CustomError;}(Error));
注意 var _this = _super.call(this, msg) || this; 中 this 被替换掉了。 在 TypeScript 2.1 的 changelog 中描述了这个 Breaking Change。 **这会造成 CustomError 的所有对象方法都无法使用,这里介绍几种 workaround:
题外话,这个分支可能会导致测试覆盖率中的 分支未覆盖问题。可以只在 ES6 下产生测试覆盖报告来解决。
1. 使用 setPrototypeOf 还原原型链
这是 TypeScript 官方给出的解决方法,见这里。
class CustomError extends Error {constructor(message) {super(message);Object.setPrototypeOf(this, FooError.prototype);}}
注意这是一个性能很差的方法,且在 ES6 中提出,兼容性也很差。在不兼容的环境下可以使用 __proto__ 来替代。
2. 坚持使用 ES5 的方式
不使用 ES6 特性,仍然使用本文前面介绍的 『ES5 中如何继承 Error?』给出的方法。
3. 限制对象方法的使用
虽然 CustomError 的对象函数无法使用,但 CustomError 仍然支持 protected 级别的方法供子类使用,阉割的地方在于自己不能调用。 由于 JavaScript 中对象属性必须在构造函数内赋值,因此对象属性也不会受到影响。也就是说:
class CustomError extends Error {count: number = 0constructor(msg) {super(msg)this.count // OK,属性不受影响this.print() // TypeError: _this.print is not a function,因为 this 被替换了}print() { console.log(this.stack)}}class DerivedError extends CustomError {constructor(msg) {super(msg)super.print() // OK,因为 print 是直接从父类原型获取的,即 `_super.prototype.print`}}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
122 在
学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..123 在
Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..原梓番博客 在
在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..博主 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..1111 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
Copyright·© 2019 侯体宗版权所有·
粤ICP备20027696号