typescript - 扩展错误类

IT技术 javascript typescript
2021-01-23 07:34:06

我试图抛出一个自定义错误,在控制台中打印我的“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 原型)?

6个回答

您是否使用 typescript 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 到此),但原型链本身无法修复。

值得注意的是,不幸的是,Object.setPrototypeOf(this, this.constructor.prototype)不会在这里工作,类必须被明确提到。
2021-03-16 07:34:06
从 TypeScript 2.2 开始,似乎有一种方法可以在构造函数中不硬编码确切类型的情况下通过:Object.setPrototypeOf(this, new.target.prototype); typescriptlang.org/docs/handbook/release-notes/...
2021-03-21 07:34:06
是的,我正在转换为 ES5。我尝试按照您的建议设置原型,但不幸的是,它不起作用。
2021-04-04 07:34:06
@JohnWeisz 如果您提到的修复有效,那么首先就没有什么需要修复的,如果您此时可以读取原型this.constructor,那么原型链就已经完好无损。
2021-04-10 07:34:06

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

这个问题可以使用从 Typescript 2.2 开始支持的“new.target”优雅地解决,请参见此处: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; } 
  }
}

Usingnew.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

我认为您可以将其设为私有,因为您不会尝试在构造函数之外访问它。所以只要你只在编译成 ES5 的 TypeScript 中使用它,应该没有问题,因为这个 TypeScript 特性只是一个编译时特性。该字段将始终在您的编译代码中公开访问。目前实际的“私人class领域”提案仅处于试验阶段。并且该__proto__属性也已弃用,并且仅进行了一定程度的标准化以在一定程度上向后兼容。有关两者的更多信息,请参阅MDN
2021-03-17 07:34:06
应该this.__proto__是私人的还是公共的?
2021-04-02 07:34:06
或者你可以简单地做类似的事情(this as any).__proto__ = actualProto;这就是我所做的。Hacky,但我不需要更改任何类声明。
2021-04-06 07:34:06
我还要补充 get [Symbol.toStringTag]() { return 'CustomError'; } static get [Symbol.species]() { return CustomError; }
2021-04-08 07:34:06

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

在您可以针对 ES2015 或更高版本之前,您可能不得不忍受不完美的输出......

正如这里的其他回答中提到的,已经有一个解决方法,所以最好接受额外的复杂性并实现原型链更新。
2021-03-29 07:34:06
这。这。这一次又一次。
2021-04-01 07:34:06

从 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
    }
}
这不会设置 .name 属性
2021-03-27 07:34:06
我相信使用this.name = new.target.name作品可以对名称进行排序,而无需对其进行硬编码。
2021-04-13 07:34:06

几天前我在我的typescript项目中遇到了同样的问题。为了使它工作,我使用MDN的实现,只使用 vanilla js。因此,您的错误将类似于以下内容:

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 控制台和我的typescript项目:

在此处输入图片说明

好吧,我也想抽象样板部分,但这样做可能失败了,我会在没有任何其他 shinanigens 的情况下再试一次。
2021-03-20 07:34:06
喜欢这个想法,未能在typescript中实现
2021-03-23 07:34:06
好主意,但strict: true在 tsconfig 中失败并显示此消息stackoverflow.com/questions/43623461/...
2021-03-24 07:34:06
它可以工作,但会破坏浏览器控制台中的错误格式。在 Chrome 控制台中,您的 CustomError 实例不会以“true”错误获得的通常更好的格式显示,而是以“通用对象”格式显示,这更难阅读。
2021-04-01 07:34:06
真的吗?它适用于我的typescript反应本机项目。
2021-04-09 07:34:06