打字稿-扩展错误类


79

我正在尝试在控制台中打印“ 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原型)?

Answers:


76

您是否正在使用打字稿版本2.1,并转换为ES5?检查重大更改页面的此部分以查找可能的问题和解决方法:https : //github.com/Microsoft/TypeScript-wiki/blob/master/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到实例),但是原型链本身无法固定。


2
是的,我正在移植到ES5。我试图按照您的建议设置原型,但不幸的是,它没有用。
darksoulsong

3
值得注意的是,不幸的是,Object.setPrototypeOf(this, this.constructor.prototype)不会在这里工作,类必须被明确提到。
John Weisz

1
@JohnWeisz如果您提到的修复程序有效,那么首先没有什么要修复的,如果您现在可以阅读原型this.constructor,那么原型链将是完整的。
thetrompf

3
作为打字稿2.2的出现似乎是一个办法做到这一点,而不通过在构造函数中硬编码的确切类型:Object.setPrototypeOf(this, new.target.prototype); typescriptlang.org/docs/handbook/release-notes/...
Grabofus

62

问题在于,当您调用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都将被破坏,例如a的实例class VeryCustomError < CustomError将不是instanceof VeryCustomError预期的,而仅仅是预期的instanceof CustomError

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


应该this.__proto__是私人的还是公共的?
lonix

我认为您可以将其设为私有,因为您不会尝试在构造函数之外访问它。因此,只要您仅在已将其编译为ES5的TypeScript中使用它,就应该没有问题,因为此TypeScript功能只是一个编译时功能。您可以在已编译的代码中公开访问该字段。目前,实际的“私人班级领域”建议仅处于试验阶段。并且__proto__不推荐使用该属性,并且仅对其进行某种程度的标准化以使其向后兼容。有关这两者的更多信息,请参见MDN
Trve

1
或者您可以简单地做类似的事情(this as any).__proto__ = actualProto;。这就是我所做的。Hacky,但是我不需要更改任何类声明。
Trve

16

它可以在ES2015(https://jsfiddle.net/x40n2gyr/)中正常运行。问题很可能是TypeScript编译器正在转换为ES5,并且Error仅使用ES5功能就不能正确地将其子类化。只能使用ES2015及更高版本的功能(class或更确切地说,Reflect.construct)正确地将其分类。这是因为当您将其Error作为函数调用时(而不是通过newES2015中的superor或Reflect.construct),它会忽略this并创建一个new Error

您可能必须忍受不完美的输出,直到可以定位到ES2015或更高版本为止。


2
这个。这个。这又是这个。
卡T


6

几天前,我在打字稿项目中遇到了相同的问题。为了使其工作,我仅使用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');

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

在此处输入图片说明


1
喜欢这个想法,但未能在打字稿中实现它
JuliusŠ。

真?它适用于我的打字稿反应本机项目。
Mμ。

好吧,我也想对样板部分进行抽象化,并且可能这样做没有成功,我将在没有任何其他shinanigens的情况下再次尝试。
朱利叶斯(JuliusŠ)。

不错的主意,但它失败,此消息时,strict: true在tsconfig:stackoverflow.com/questions/43623461/...
安德烈

它可以工作,但是会破坏浏览器控制台中的错误格式。在Chrome控制台中,您的CustomError实例不会以“ true”错误的通常更好的格式显示,而是以“通用对象”格式显示,这很难阅读。
克里斯蒂安·汉纳坎普

0

试试这个...

class CustomError extends Error { 

  constructor(message: string) {
    super(`Lorem "${message}" ipsum dolor.`)
  }

  get name() { return this.constructor.name }

}

throw new CustomError('foo')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.