Typescript - 扩展错误类

dar*_*ong 57 javascript typescript

我试图抛出一个自定义错误,我的"CustomError"类名称打印在控制台而不是"错误",但没有成功:

class CustomError extends Error { 
    constructor(message: string) {
      super(`Lorem "${message}" ipsum dolor.`);
      this.name = 'CustomError';
    }
}
throw new CustomError('foo'); 
Run Code Online (Sandbox Code Playgroud)

输出是Uncaught Error: Lorem "foo" ipsum dolor.

我的期望:Uncaught CustomError: Lorem "foo" ipsum dolor.

我想知道是否只能使用TS来完成(没有搞乱JS原型)?

Vid*_*dar 56

您使用的是打字稿版本2.1,并转换为ES5吗?请查看重大更改页面的此部分,以了解可能的问题和解决方法:https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-和地图-MAY-不再视为工作

相关位:

作为建议,您可以在任何超级(...)调用后立即手动调整原型.

class FooError extends Error {
    constructor(m: string) {
        super(m);

        // Set the prototype explicitly.
        Object.setPrototypeOf(this, FooError.prototype);
    }

    sayHello() {
        return "hello " + this.message;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,FooError的任何子类都必须手动设置原型.对于不支持Object.setPrototypeOf的运行时,您可以使用__proto__.

不幸的是,这些变通办法不适用于Internet Explorer 10及更早版本.可以手动将原型中的方法复制到实例本身(即FooError.prototype到此),但原型链本身无法修复.

  • 值得注意的是,不幸的是,`Object.setPrototypeOf(this,this.constructor.prototype)`将在这里工作,必须明确引用该类. (3认同)
  • 从 TypeScript 2.2 开始,似乎有一种方法可以做到这一点,而无需通过以下方式在构造函数中硬编码确切类型:`Object.setPrototypeOf(this, new.target.prototype);` https://www.typescriptlang.org/docs/手册/发行说明/typescript-2-2.html#example (3认同)
  • 是的,我正在转向ES5.我按照你的建议尝试设置原型,但不幸的是,它没有用. (2认同)

Kri*_*amp 46

问题是Javascript的内置类Error通过将要构造的对象(即this)转换为新的不同对象来打破原型链,当您调用super并且新对象没有预期的原型链时,即它是一个实例的Error不是CustomError.

使用'new.target'可以很好地解决这个问题,这是自TypesScript 2.2以来支持的,请看这里:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html

class CustomError extends Error {
  constructor(message?: string) {
    // 'Error' breaks prototype chain here
    super(message); 

    // restore prototype chain   
    const actualProto = new.target.prototype;

    if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); } 
    else { this.__proto__ = actualProto; } 
  }
}
Run Code Online (Sandbox Code Playgroud)

使用new.target的优势在于您无需对原型进行硬编码,就像此处提出的其他一些答案一样.这再次具有以下优点:继承的类CustomError将自动获得正确的原型链.

如果你要对原型进行硬编码(例如Object.setPrototype(this, CustomError.prototype)),CustomError它本身就会有一个工作原型链,但是任何继承的类CustomError都会被破坏,例如a的实例class VeryCustomError < CustomError不会instanceof VeryCustomError像预期的那样,而只是instanceof CustomError.

另见:https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200

  • 我还会添加 `get [Symbol.toStringTag]() { return 'CustomError'; }` `静态 get [Symbol.species]() { return CustomError; }` (2认同)

Gra*_*fus 15

从 TypeScript 2.2 开始,它可以通过new.target.prototype. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#example

class CustomError extends Error {
    constructor(message?: string) {
        super(message); // 'Error' breaks prototype chain here
        this.name = 'CustomError';
        Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我相信使用 `this.name = new.target.name` 可以对名称进行排序,而无需对其进行硬编码。 (3认同)

T.J*_*der 10

它在ES2015(https://jsfiddle.net/x40n2gyr/)中正常工作.最有可能的问题是,TypeScript编译器正在转换为ES5,并且Error仅使用ES5功能无法正确子类化; 它只能使用ES2015及以上功能正确地进行子类化(class或者,更加模糊不清Reflect.construct).这是因为,当你调用Error一个函数(而不是通过new或在ES2015,superReflect.construct),它忽略this并创建一个新的 Error.

你可能不得不忍受不完美的输出,直到你可以瞄准ES2015或更高......

  • 这。这。这样又这样。 (3认同)

小智 10

我实际上从未在 SO 上发过帖子,但我的团队正在开发一个 TypeScript 项目,我们需要创建许多自定义错误类,同时还针对 es5。在每个错误类别中执行建议的修复将是非常乏味的。extend但我们发现,通过创建一个主要的自定义错误类,并将其余错误置于该类中,我们能够对所有后续错误类产生下游影响。在该主错误类内部,我们执行了以下操作以产生更新原型的下游效果:

class MainErrorClass extends Error {
  constructor() {
    super()
    Object.setPrototypeOf(this, new.target.prototype)
  }
}

class SomeNewError extends MainErrorClass {} 

...
Run Code Online (Sandbox Code Playgroud)

使用new.target.prototype是更新所有继承错误类而无需更新每个错误类的构造函数的关键。

只是希望这能在未来避免其他人的头痛!


Mμ.*_*Mμ. 5

几天前我在打字稿项目中遇到了同样的问题.为了使它工作,我使用仅使用vanilla js的MDN实现.所以你的错误看起来像下面这样:

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || 'Default Message';
  this.stack = (new Error()).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;

throw new CustomError('foo');
Run Code Online (Sandbox Code Playgroud)

它似乎不适用于SO代码片段,但它在chrome控制台和我的打字稿项目中有效:

在此输入图像描述