我试图抛出一个自定义错误,并在控制台中打印我的“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 原型)?
您使用的是 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 到此实例上),但原型链本身无法修复。
问题是 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
从 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
}
}
它在 ES2015 中可以正常工作(https://jsfiddle.net/x40n2gyr/)。最有可能的问题是 TypeScript 编译器正在转换为 ES5,并且仅使用 ES5 功能无法正确对
Error
进行子类化;它只能使用 ES2015 及以上功能正确地进行子类化(class
,或者更晦涩的是,Reflect.construct
)。这是因为当您将 Error
作为函数调用时(而不是通过 new
或在 ES2015 中,super
或 Reflect.construct
),它会忽略 this
并创建一个 new Error
。
您可能不得不忍受不完美的输出,直到您能够以 ES2015 或更高版本为目标...
我从来没有在 SO 上发过帖子,但我的团队正在开发一个 TypeScript 项目,我们需要创建许多自定义错误类,同时也针对 es5。在每个错误类别中执行建议的修复将是非常乏味的。但我们发现,通过创建一个主要的自定义错误类,并将其余错误置于该类中,我们能够对所有后续错误类产生下游影响。在该主错误类内部,我们执行了以下操作以产生更新原型的下游效果:
extend
使用
class MainErrorClass extends Error {
constructor() {
super()
Object.setPrototypeOf(this, new.target.prototype)
}
}
class SomeNewError extends MainErrorClass {}
...
是更新所有继承错误类而无需更新每个错误类的构造函数的关键。
只是希望这能在未来避免其他人的头痛!
将 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');
"target": "es2017"