I'm trying to throw a custom error with my "CustomError" class name printed in the console instead of "Error", with no success:
class CustomError extends Error {
constructor(message: string) {
super(`Lorem "${message}" ipsum dolor.`);
this.name = 'CustomError';
}
}
throw new CustomError('foo');
The output is Uncaught Error: Lorem "foo" ipsum dolor
.
What I expect: Uncaught CustomError: Lorem "foo" ipsum dolor
.
I wonder if that can be done using TS only (without messing with JS prototypes)?
Just like object-oriented languages such as Java and C#, TypeScript classes can be extended to create new classes with inheritance, using the keyword extends . In the above example, the Employee class extends the Person class using extends keyword.
To declare a function that throws an error, set its return type to never . The never type is used for functions that never return a value, in other words functions that throw an exception or terminate execution of the program.
Are you using typescript version 2.1, and transpiling to ES5? Check this section of the breaking changes page for possible issues and workaround: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
The relevant bit:
As a recommendation, you can manually adjust the prototype immediately after any super(...) calls.
class FooError extends Error { constructor(m: string) { super(m); // Set the prototype explicitly. Object.setPrototypeOf(this, FooError.prototype); } sayHello() { return "hello " + this.message; } }
However, any subclass of FooError will have to manually set the prototype as well. For runtimes that don't support Object.setPrototypeOf, you may instead be able to use
__proto__
.Unfortunately, these workarounds will not work on Internet Explorer 10 and prior. One can manually copy methods from the prototype onto the instance itself (i.e. FooError.prototype onto this), but the prototype chain itself cannot be fixed.
The problem is that Javascript's built-in class Error
breaks the prototype chain by switching the object to be constructed (i.e. this
) to a new, different object, when you call super
and that new object doesn't have the expected prototype chain, i.e. it's an instance of Error
not of CustomError
.
This problem can be elegantly solved using 'new.target', which is supported since Typescript 2.2, see here: 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; }
}
}
Using new.target
has the advantage that you don't have to hardcode the prototype, like some other answers here proposed. That again has the advantage that classes inheriting from CustomError
will automatically also get the correct prototype chain.
If you were to hardcode the prototype (e.g. Object.setPrototype(this, CustomError.prototype)
), CustomError
itself would have a working prototype chain, but any classes inheriting from CustomError
would be broken, e.g. instances of a class VeryCustomError < CustomError
would not be instanceof VeryCustomError
as expected, but only instanceof CustomError
.
See also: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
It works correctly in ES2015 (https://jsfiddle.net/x40n2gyr/). Most likely, the problem is that the TypeScript compiler is transpiling to ES5, and Error
cannot be correctly subclassed using only ES5 features; it can only be correctly subclassed using ES2015 and above features (class
or, more obscurely, Reflect.construct
). This is because when you call Error
as a function (rather than via new
or, in ES2015, super
or Reflect.construct
), it ignores this
and creates a new Error
.
You'll probably have to live with the imperfect output until you can target ES2015 or higher...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With