Typescript - 扩展错误类

问题描述 投票:0回答:8

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

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

输出是

Uncaught Error: Lorem "foo" ipsum dolor

我的期望:

Uncaught CustomError: Lorem "foo" ipsum dolor

我想知道是否可以仅使用 TS 来完成(而不弄乱 JS 原型)?

javascript typescript
8个回答
123
投票

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

相关部分:

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

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

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

    sayHello() {
        return "hello " + this.message;
    }
}

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

__proto__

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


102
投票

问题是 Javascript 的内置类

Error
通过将要构造的对象(即
this
)切换到一个新的不同对象来破坏原型链,当你调用
super
并且新对象不这样做时具有预期的原型链,即它是
Error
的实例,而不是
CustomError
的实例。

这个问题可以使用'new.target'来优雅地解决,从 Typescript 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; } 
  }
}

使用

new.target
的优点是您不必对原型进行硬编码,就像这里提出的其他一些答案一样。这又具有一个优点,即从
CustomError
继承的类也将自动获得正确的原型链。

如果你要对原型进行硬编码(例如

Object.setPrototype(this, CustomError.prototype)
),
CustomError
本身将有一个工作原型链,但是从
CustomError
继承的任何类都会被破坏,例如
class VeryCustomError < CustomError
的实例不会是预期的
instanceof VeryCustomError
,而只是
instanceof CustomError

另请参阅:https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200


32
投票

从 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
    }
}

31
投票

它在 ES2015 中可以正常工作(https://jsfiddle.net/x40n2gyr/)。最有可能的问题是 TypeScript 编译器正在转换为 ES5,并且仅使用 ES5 功能无法正确对

Error
进行子类化;它只能使用 ES2015 及以上功能正确地进行子类化(
class
,或者更晦涩的是,
Reflect.construct
)。这是因为当您将
Error
作为函数调用时(而不是通过
new
或在 ES2015 中,
super
Reflect.construct
),它会忽略
this
并创建一个 new
Error

您可能不得不忍受不完美的输出,直到您能够以 ES2015 或更高版本为目标...


10
投票

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

extend

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

是更新所有继承错误类而无需更新每个错误类的构造函数的关键。

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


7
投票
MDN

的实现,仅使用 vanilla js。所以你的错误看起来像下面这样:

new.target.prototype

它似乎在 SO 代码片段中不起作用,但在 chrome 控制台和我的打字稿项目中却起作用:

enter image description here


3
投票

将 tsconfig 编辑为

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');



1
投票

"target": "es2017"

	
© www.soinside.com 2019 - 2024. All rights reserved.