The issue arises due to the way Javascript's built-in class Error
disrupts the prototype chain by altering the object being constructed (i.e. this
) to a new, distinct object when super
is called. This new object does not have the expected prototype chain, as it becomes an instance of Error
instead of CustomError
.
An elegant solution to this problem is using 'new.target', which has been available since Typescript 2.2. More information can be found here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html
class CustomError extends Error {
constructor(message?: string) {
// 'Error' disrupts the prototype chain at this point
super(message);
// restoring the prototype chain
const actualProto = new.target.prototype;
if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); }
else { this.__proto__ = actualProto; }
}
}
The use of new.target
eliminates the need to hardcode the prototype, unlike some previous suggestions in other answers. This approach ensures that classes inheriting from CustomError
automatically inherit the correct prototype chain.
If the prototype were hardcoded (e.g.
Object.setPrototype(this, CustomError.prototype)
), although
CustomError
itself would have the proper prototype chain, any subclasses of
CustomError
could encounter issues. For example, instances of a
class VeryCustomError < CustomError
might not be recognized as
instanceof VeryCustomError
but only as
instanceof CustomError
as intended.
For more details, refer to: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200