Typescript - Extending Error class - javascript

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)?

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

As of TypeScript 2.2 it can be done via 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
}
}

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

I literally never post on SO, but my team is working on a TypeScript project, and we needed to create many custom error classes, while also targeting es5. It would have been incredibly tedious to do the suggested fix in every single error class. But we found that we were able to have a downstream effect on all subsequent error classes by creating a main custom error class, and having the rest of our errors extend that class. Inside of that main error class we did the following to have that downstream effect of updating the prototype:
class MainErrorClass extends Error {
constructor() {
super()
Object.setPrototypeOf(this, new.target.prototype)
}
}
class SomeNewError extends MainErrorClass {}
...
Using new.target.prototype was the key to getting all of the inheriting error classes to be updated without needing to update the constructor of each one.
Just hoping this saves someone else a headache in the future!

I ran into the same problem in my typescript project a few days ago. To make it work, I use the implementation from MDN using only vanilla js. So your error would look something like the following:
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');
It doesn't seem to work in SO code snippet, but it does in the chrome console and in my typescript project:

I was having this problem in a nodejs server. what worked for me was to transpile down to es2017 in which these issues seems to be fixed.
Edit tsconfig to
"target": "es2017"

Try this...
class CustomError extends Error {
constructor(message: string) {
super(`Lorem "${message}" ipsum dolor.`)
}
get name() { return this.constructor.name }
}
throw new CustomError('foo')

Related

Correctly declare static variables in JavaScript classes

In my code, I do the following (very simplified):
class AddOrSelectAddress {
static body; // <-- Error
static async open() {
await $.get(basePath + 'Manage/AddOrSelectAddress', null, result => {
this.body = document.createElement('div');
this.body.innerHTML = result;
});
// ...
}
static someOtherMethod() {
// do something with body
}
}
My code works fine in Chrome. Firefox, though, complaints an error in the second line of code:
SyntaxError: bad method definition
I'm relatively new to class-based JavaScript programming. What am I doing wrong here?
Static variables in JavaScript doesn't really help me, because it mainly uses old syntax.
Static class fields are a stage 3 proposal, meaning they're not yet an official part of the JavaScript language. (Stage 4 is the final stage.) You can read more about the proposal here and the proposal process here.
Currently, Chrome (as of version 72) is the only browser that supports static class fields.
To use this feature in other browsers you would need to use Babel with #babel/plugin-proposal-class-properties to transpile your code. If you're not already using Babel, however, this might be overkill.
Alternatively, you can assign a property to the class after initializing it. This isn't semantically identical, but works for your (and, indeed, most) use cases.
class AddOrSelectAddress {
// ...
}
AddOrSelectAddress.body = 'some initial value';
You can see this working in the below snippet.
class AddOrSelectAddress {
static changeBody(val) {
this.body = val;
}
static someMethod() {
console.log('in someMethod body is', this.body);
}
static someOtherMethod() {
console.log('in someOtherMethod body is', this.body);
}
}
AddOrSelectAddress.body = 'some initial value';
AddOrSelectAddress.someMethod();
AddOrSelectAddress.changeBody('some other value');
AddOrSelectAddress.someOtherMethod();
If you don't want to set an initial value for body then you could just omit the line (since accessing a nonexistent property of an object returns undefined), or you could explicitly set it to undefined.
Static methods are perfectly fine to use. However static properties are a recent addition that dont work in all browsers yet. It works in Chrome but like you said not in firefox. Please take a look at this article as it backs up my answer : https://javascript.info/static-properties-methods. To fix your issue you could declare the variable inside your static method.

Using ES6 Class syntax, how to implement class methods from other files?

A class I am writing in NodeJS v8.10 (Webpack built) looks like its about to get really large. I want to break the methods out to their own files, but I also want to maintain the ES6 Class syntax since I come from an OOP background.
Is there a nicer ES6 syntax to implement methods of a class from other files?
I am presently extending the prototype per the code below, but it would be nice to have everything within the class braces "{}".
const fnClose = require('./close');
// about 20 more methods required in here
class Door {
constructor() {}
// close: require('./close'); // this would be nice!
}
/*
it doesn't seem to matter if the exports line comes
BEFORE the prototype extensions; it still exports the
'close' method with the Door class.
*/
// module.exports = Door; // works, just looks wrong here
Door.prototype.close = fnClose;
// about 20 more methods added to class here
module.exports = Door; // the natural place for exports
UPDATE
Based on the spark that Oliver provided in his answer below, this code can be refactored to bring the methods "inside the braces" like so. This isn't as "ES6" as I was hoping; a cleaner syntax would be nice. But this does get the job done!
const fnClose = require('./close');
// about 20 more methods required in here
class Door {
constructor(...args) {
// PROPERTIES
this.species = 'Oak';
// METHODS - FROM THEIR OWN FILES!
this.close = fnClose; // how to close the door
// CONSTRUCTOR CODE
// <do stuff with args>
}
}
module.exports = Door;
/*
And thats it. everything tucked inside the
class, no need for prototype extenstions.
Does not appear to be a need for Babel though.
*/
As James Thorpe indicates, it may be that your class itself is growing too large. That being said, if you're using babel, then you can class fields to achieve something that, at least as far as I can see, will achieve the same effect:
function test() {
console.log('called test')
console.log(this.value)
}
class TestClass {
value = "This is the test value";
runTest = test;
}
const x = new TestClass();
x.runTest()
Without babel, you cannot use class variables, as they aren't supported in js just yet. There is a proposal which is at stage 3 at the time of writing, and babel can transpile it for us.
The snippet above is using babel to get things to work. You ask in your comment whether babel is just converting this to the same code as you have. It's similar, but different in a few key ways. Babel transpiles it to this (using their sandbox):
"use strict";
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function test() {
console.log('called test');
console.log(this.value);
}
var TestClass = function TestClass() {
_classCallCheck(this, TestClass);
this.value = "This is the test value";
this.runTest = test;
};
var x = new TestClass();
x.runTest();
So it isn't using the class syntax at all. It's useful to remember that class in javascript is just syntactic sugar in any case, so something similar to this is going on behind the scenes when you use class in any case.
Babel does seem to require a plugin for this, details can be found here.

Combine Array with Error in a single type

I have a promise-based library (for Node.js 0.10 - 6.x) with a method that rejects with an Array.
When using Bluebird it results in a warning: a promise was rejected with a non-error.
Wrapping the array into a custom error type is easy, but I want to avoid breaking the library's backward compatibility.
Is it possible to implement such an object that could be used as an array, while being seen by Bluebird as an Error object at the same time?
extras
When inheriting from Error I use the following helper for compatibility with Node.js 0.10 - 0.12:
function inherits(child, parent) {
child.prototype.__proto__ = parent.prototype;
}
And looking at the Bluebird source, maybe there is a way to circumvent its verification somehow:
Promise.prototype._rejectCallback =
function(reason, synchronous, ignoreNonErrorWarnings) {
var trace = util.ensureErrorObject(reason);
var hasStack = trace === reason;
if (!hasStack && !ignoreNonErrorWarnings && debug.warnings()) {
var message = "a promise was rejected with a non-error: " +
util.classString(reason);
this._warn(message, true);
}
this._attachExtraTrace(trace, synchronous ? hasStack : false);
this._reject(reason);
};
Javascript don't permits real multiple prototypal inheritance.
You can "extend" Error but the subclass can't be both instanceof an Error and an Array.
If Bluebird use duck-typing you can try to subclass Array object and simulate Error behavior (interface and and properties), but this depends strongly of Bluebird error-check implementation.
I think is better and robust to wrap the array value into an Error property/attribute.
So long as your environment allows subclassing Error, yes!
I asked about something similar while extending errors to create my own subtypes, which is essentially what you want to do. In fact, I've done something similar to create HTTP-specific errors that include the response's status code.
Assuming your environment does allow class SubError extends Error (and there is a workaround if not), you simply do:
class ErrorWithData {
constructor(msg, data = []) {
super(msg); // this changes with the workaround
this._data = [].concat(data); // make a copy for safety's sake
}
get data() {
return this._data;
}
}
If you're in an older browser that does not allow you to subclass Error, you can use the shim class from #Mosho's answer and replace extends Error in that example with extends ErrorClass.
NodeJS will allow you to extends Error from v4 and on, with v6 having correct/full support for it. Earlier versions require Mosho's workaround.

Anonymous/inline interface implementation in TypeScript

I've just started with TypeScript and I'm trying to understand why the following inline object definition isn't considered valid. I have a collection of objects - their type is irrelevant (to me), but they implement the interface so that when I iterate through them I know that the interface methods will be present in each object in the collection.
I came across a "compiler" error when I tried to create an object with private information required to implement the required method:
interface Doable {
do();
}
function doThatThing (doableThing: Doable) {
doableThing.do();
}
doThatThing({
private message: 'ahoy-hoy!', // compiler error here
do: () => {
alert(this.message);
}
});
The compiler error message is "Argument of type '{ message: string, do: () => void; }' is not assignable to type Doable. Object literal must specify known properties, and 'message' does not exist in type Doable". Note that the same message is given if I define the object outside of the function call, i.e.
var thing: Doable;
thing = {
private message: 'ahoy-hoy!', // error here
do: () => {
alert(this.message);
}
};
doThatThing(thing);
The same error occurs if I add "unexpected" methods as well:
doThatThing({
do: () => {
alert("ahoy hoy");
},
doSecretly: () => { // compiler error here now
alert("hi there");
}
});
I looked at the JavaScript and discovered that this within the inline object definition was being scoped to the global object:
var _this = this; // wait, no, why!?
function doThatThing(doableThing) {
doableThing.do();
}
doThatThing({
message: 'ahoy-hoy!',
do: function () {
alert(_this.message); // uses global
}
});
I tried searching for information on inline implementations of interfaces in TypeScript, but couldn't find anything speaking to this issue specifically.
I can confirm that the "fixed" compiled JS works as intended:
function doThatThing(doableThing) {
doableThing.do();
}
doThatThing({
message: 'ahoy-hoy!',
do: function () {
alert(this.message);
}
});
...and that makes sense to me, because (as far as I understand) this is implicitly calling the Object constructor, so this should be scoped to the new Object instance.
It seems like the only solution is to declare each implementation as a class implementing the interface, but that feels really regressive/heavy-handed since I'm only going to have one instance of each class. If the only contract with the called function is implementing the interface, then why can't the object contain additional members?
Sorry, this turned out longer than I intended ...in summary, I'm asking:
Why is that inline interface implementation ("anonymous class", as would be said in Java) considered invalid in TypeScript? Specifically, what does that compiler error mean, and what does it protect against?
Why is the scope-reassignment to the global object generated in the "compiled" JavaScript?
Assuming it's my error (e.g. that the compiler error is necessary for protecting against some undesirable condition), is the only solution really to explicitly declare a class in advance, like so?
interface Doable {
do() : void;
}
class DoableThingA implements Doable { // would prefer to avoid this ...
private message: string = 'ahoy-hoy';
do() {
alert(this.message);
}
}
class DoableThingB implements Doable { // ... as well as this, since there will be only one instance of each
do() {
document.getElementById("example").innerHTML = 'whatever';
}
}
function doThatThing (doableThing: Doable) {
doableThing.do();
}
var things: Array<Doable>;
things = new Array<Doable>();
things.push(new DoableThingA());
things.push(new DoableThingB());
for (var i = 0; i < things.length; i++) {
doThatThing(things[i]);
}
P.S. The compiler error only appeared when I upgraded to TS 1.6 today, although the faulty scope bug in the compiled JS occurs in both 1.6 and 1.5.
Update: François Cardinaux provided a link to this answer, which recommends using a type assertion, but this only removes the compiler error and actually causes a logic error due to improper scope:
interface Doable {
do();
}
function doThatThing (doableThing: Doable) {
doableThing.do();
}
doThatThing(<Doable>{ // assert that this object is a Doable
private message: 'ahoy-hoy!', // no more compiler error here
do: () => {
alert(this.message);
}
});
Looking at the compiled JS, this is incorrect:
var _this = this; // very wrong, and now hidden
function doThatThing(doableThing) {
doableThing.do();
}
doThatThing({
message: 'ahoy-hoy!',
do: function () {
alert(_this.message); // s/b "this.message", which works in JS (try it)
}
});
OK, I finally discovered the problem to question 2 - I was using the fat arrow => to declare the object's method here:
doThatThing(<Doable>{
private message: 'ahoy-hoy!',
do: () => { // using fat arrow: global scope replaces new object's scope
alert(this.message);
}
});
...which "sucked" the global scope into the method. The problem is fixed using the longer syntax, like so:
doThatThing(<Doable>{
private message: 'ahoy-hoy!',
do: function() { // using "regular" anonymous function syntax, "this" meaning is preserved
alert(this.message);
}
});
So in summary:
unanswered;
There was a typo in my code, and I should have been using "function()" instead of "=>"; and,
Type-asserting the object with the interface removes the compiler error.

Throwing custom exceptions in Javascript. Which style to use?

Douglas Crockford recommends doing something like this:
throw {
name: "System Error",
message: "Something horrible happened."
};
But you could also do something like this:
function IllegalArgumentException(message) {
this.message = message;
}
throw new IllegalArgumentException("Argument cannot be less than zero");
and then do:
try {
//some code that generates exceptions
} catch(e) {
if(e instanceof IllegalArgumentException) {
//handle this
} else if(e instanceof SomeOtherTypeOfException) {
//handle this
}
}
I guess you could include a type property in Crockford's implementation and then examine that instead of doing an instanceof. Is there any advantage from doing one versus the other?
Update 2022
If your environment supports ES6, you should use inheritance from Error class, as recommended by Mozilla:
class IllegalArgumentException extends Error {
// ...
}
This is also the most upvoted answer in What's a good way to extend Error in JavaScript?.
Pre-ES6 (original answer)
Also pre-ES6 environments provide the Error class as basis for exceptions. It already allows you to define a message, but also provides a useful stack property to track down the context of the exception.
You can create your own exception type by using prototypical inheritance. There are already several stackoverflow discussions (for example: here), how to do this properly. However, I had to dig a little bit until I found the correct and modern approach. Please be aware that the approach that is suggested in the Mozilla documentation (see above) is not liked by the stackoverflow community. After a lot of reading I came out with that approach for inherit from Error.prototype:
function IllegalArgumentException(sMessage) {
this.name = "IllegalArgumentException";
this.message = sMessage;
this.stack = (new Error()).stack;
}
IllegalArgumentException.prototype = Object.create(Error.prototype);
IllegalArgumentException.prototype.constructor = IllegalArgumentException;
I am in favor of the second one since it is more reusable in terms of code purity. To be precise, if I am about to throw the same exception (even the same exception type with a different message) on several places, my code would get messy (immense) with the first approach.

Categories