JavaScript - How to get child class name when extending Error - javascript

I created some custom Erros in my application and I wanted to check for them later using the constructor name. The problem is that when I extend Error in my classes, the constructor.name is always "Error", not the name I actually gave to it.
I was doing some tests and noticed that this happens with the Error class, but not with any other custom class I create.
e.g.:
class CustomClass {
constructor(msg) {}
}
class OtherClass extends CustomClass {
constructor(msg) {
super(msg);
}
class CustomError extends Error {
constructor(msg) {
super(msg);
}
}
const e = new CustomError("There was an error");
const otherClass = new OtherClass("This is a class");
console.log(otherClass.constructor.name); // "OtherClass" <- ok!
console.log(e.constructor.name); // "Error" <- not ok! expected "CustomError"
Does anyone knows why this happens?
I thought I could do something like:
class CustomError extends Error {
constructor(msg) {
super(msg);
}
getName() {
return "CustomError";
}
}
const e = new CustomError("There was an error");
if(e.getName() == "CustomError") {
// do stuff
}
But then I get: TypeError: e.getName is not a function
Does anyone know if there is a way to override the constructor name when extending Error?
Also, why can't I declare and call methods in my CustomError error class?
EDIT
Following #samanime suggestion, I updated my node version to 8.8.1 and was able to find a partial solution.
Changing the syntax a little bit I got:
const FileSystemException = module.exports = class FileSystemException extends Error {
constructor(msg) {
super(msg);
}
getName() {
return "FileSystemException";
}
}
const e = new FileSystemException("There was an error");
// Running node app.js
console.log(e.constructor.name); // "FileSystemException"
console.log(e.getName()); // "FileSystemException"
// Running babel-node app.js
console.log(e.constructor.name); // "Error"
console.log(e.getName()); // "TypeError: e.getName is not a function"
Still, it would be awesome if someone could make it work with babel so I can use import/export statements without having to wait for node v9.4 LTS.
Using:
node v8.8.1
babel-node v6.26.0 w/ "es2015" and "stage-0" presets
thanks!

It seems to work just fine in this simple example:
class CustomError extends Error {
constructor(msg) {
super(msg);
}
}
const error = new CustomError()
console.log(error.constructor.name);
console.log(error instanceof CustomError);
console.log(error instanceof Error);
That is running with native class support (in Chrome).
It is possible that it isn't an issue with your syntax, but an issue with your transpiler. You probably want to dig through the transpiled code itself and see if it is doing anything special with Error that it doesn't with normal classes.
The fact that your getName() example didn't work also seems to indicate something funky is going on. Your examples you've posted look good. Double-check the code you are trying to run actually matches them.

A simple solution could be as follow
class CustomError extends Error {
constructor(msg) {
super(msg);
}
get name(){
return 'CustomError'
}
}
const e = new CustomError("There was an error");
console.log(e.constructor.name); // "CustomError"
console.log(e.name); // "CustomError"
This will also change the error name in the stack trace so when you throw your custom error object you will see:
CustomError: some random stack trace
Instead of:
Error: some random stack trace
For more information about using 'get' in js classes/objects, you can take a look at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
Hope I am not too late to the party and that you find this helpful.

To make error.name correctly set, you can create a base class for all your custom errors.
class MyAbstractError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
class NotFoundError extends MyAbstractError {}
class BadParameterError extends MyAbstractError {}
console.log( (new BadParameter("x")).name) // => "BadParameter"
console.log( (new NotFoundError ("x")).name) // => "NotFoundError"
Or you can simply add a name() getter in your base class that returns this.constructor.name

Related

Javascript - Create custom errors and make sure that constructor params are valid

I have defined the following object
const AuthErrorCode = {
EMAIL_ALREADY_EXISTS: {
code: "auth/email-already-exists",
message: "Hello world!"
},
... more error codes
};
And I am implementing a class that extends Error
class AuthError extends Error {
constructor(code, message = undefined) {
switch(code) {
case "EMAIL_ALREADY_EXISTS":
code = AuthErrorCode[code].code;
message = message ?? AuthErrorCode[code].message;
break;
default:
throw new Error("Invalid code");
}
super(message);
Object.assign(this, {
code,
name: "AuthError",
});
}
}
which is supposed to receive a code and an optional custom message.
This class has to check that the given code is in the AuthErrorCode object (EMAIL_ALREADY_EXISTS || "auth/email-already-exists" are valid). If it is not inside it, then some kind of feedback should be displayed to the programmer (an error or something). I mean, I need to make sure that the code is a valid AuthErrorCode, because if not, the class is being used incorrectly.
How can I do that? Is it possible?
For example, this code must fail:
throw new AuthError("auth/some-invented-code", "Hello world!");
Example of correct use:
throw new AuthError("EMAIL_ALREADY_EXISTS", "Hello world!");
throw new AuthError("auth/email-already-exists");
There's a way but ultimately I think it's bad design.
If you already know the set of valid values why not simply expose factory functions for every error types?
e.g.
// Assume encapsulated in a module
class AuthError extends Error {
constructor(message, code) {
super(message);
this.code = code;
}
}
// This would get exported (factory methods for errors)
const authErrors = Object.freeze({
emailAlreadyExists(message = 'Hello world!') {
return new AuthError('auth/email-already-exists', message);
}
// other error types
});
// Client usage
throw authErrors.emailAlreadyExists('email already registered');
Alternatively you could create an explicit exception class per error type whic is perhaps more aligned with the Open-Closed Principle:
// Assume encapsulated in a module
class AuthError extends Error {
constructor(message, code) {
super(message);
this.code = code;
}
}
// Export this
class EmailAdreadyExists extends AuthError {
constructor(message = "Hello world!") {
super(message, "auth/email-already-exists");
}
}
// Client usage
throw new EmailAdreadyExists("email already registered");

How to implement custom Error class in typescript?

I having been doing Swift Development for past 3 years and recently switch to typescript.
In Swift an error of type NSError and which gives us some properties.
In JavaScript an error is of any.
Why error is of any? How can I create custom class with some properties and use it in whole project?
try {
const decodedResult = decode(token)
// Adding userId in the headers to use in the models.
req.headers.userId = decodedResult.paylod.id
next()
} catch (error) {
console.log(error)
new errorController(401, 'Authentication required.', res)
}
Now error object here is of type any. I want it to be strongly typed.
Any are the type of variables that we do not know when we are writing
an application. These values may come from dynamic content, e.g. from
the user or a 3rd party library. In these cases, we want to opt-out of
type checking and let the values pass through compile-time checks. To
do so, we label these with the any type.
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
You can extend Error Class in typescript to create your custom error handler
class MyError extends Error {
constructor(m: string) {
super(m);
}
anyFunction() {
return "Hello World " ;
}
}
try {
const decodedResult = decode(token)
// Adding userId in the headers to use in the models.
req.headers.userId = decodedResult.paylod.id
next()
} catch (error) {
console.log(error)
throw new MyError() // pass arguments of constructor if any
}
See Reference
As mentioned in https://basarat.gitbooks.io/typescript/docs/types/exceptions.html you can throw something like new Error() (as well as any other thing because it will be transpilled into javascirpt at the end)
Hence you could create a new class such as
export class MySpecificError extends Error
That you could throw in you methods and catch in your catchclauses
However this will only work for code you write yourself
Thats because you can throw anything in Javascript:
throw 1;
As any deeply nested code could throw something somewhere, it is impossible for Typescript to correctly type it, thats why "error" has an any type.
Typeguards however help you to narrow down a specific error type:
class CustomError extends Error {
constructor(public code: number, message: string) {
super(message);
}
}
try {
throw new CustomError(404, "Not Found");
} catch(error) {
if(!(error instanceof CustomError))
throw error;
console.log(error.code); // correctly typed here
}

How to check if Child class has overridden Parent method/function?

I'm mocking an interface class:
const error = "Child must implement method";
class MyInterface
{
normalFunction()
{
throw error;
}
async asyncFunction()
{
return new Promise(() => Promise.reject(error));
}
}
class MyImplementation extends MyInterface
{
}
If any of the interface methods are called without an overridden implementation, the error gets thrown. However these errors will only appear at time of execution.
Is there a way to check that the functions were overridden at construction?
You could add some inspection in the constructor of MyInterface, like this:
class MyInterface {
constructor() {
const proto = Object.getPrototypeOf(this);
const superProto = MyInterface.prototype;
const missing = Object.getOwnPropertyNames(superProto).find(name =>
typeof superProto[name] === "function" && !proto.hasOwnProperty(name)
);
if (missing) throw new TypeError(`${this.constructor.name} needs to implement ${missing}`);
}
normalFunction() {}
async asyncFunction() {}
}
class MyImplementation extends MyInterface {}
// Trigger the error:
new MyImplementation();
Note that there are still ways to create an instance of MyImplementation without running a constructor:
Object.create(MyImplementation.prototype)
Can't you use reflection in order to list all the function of a class?
For example here https://stackoverflow.com/a/31055217/10691359 give us a function which list all function of an object. Once you get all of them, you can see if you have or not an overridden function.

Nodejs custom error class throws "TypeError: ValidationError is not a constructor"

Looking at some of the other questions on constructing classes, I can't figure out what I'm doing wrong here.
I have a custom error class called ValidationError that lives in the file validationError.js:
class ValidationError extends Error {
constructor(message, errors) {
super(message);
this.errors = errors;
this.name = this.constructor.name;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = (new Error(message)).stack;
}
}
}
module.exports = ValidationError;
I require this class in another file like so:
const { ValidationError } = require('./validationError');
And call it like this, which is the line that throws the error:
const validationError = new ValidationError('JSON failed validation.', result.errors);
The thrown error is, "TypeError: ValidationError is not a constructor".
I am on Node 10.6.4.
So what am I doing wrong here? Thanks for the help!
You're not exporting an object with a .ValidationError constructor, you're directly setting the class as the module.exports. So in your import it should be
const ValidationError = require('./validationError');
and not use destructuring syntax.

Bluebird filtered catch with babel and extending Error class

I'am running a nodejs (server) project (v6.11.2) and on that project I have defined custom errors by extending the Error class. I use babel to compile, the config for babel I use is:
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
]
}
The AppError class:
export default class AppError extends Error {
constructor (message, status) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
this.status = status || 500;
}
}
I want to check if the cancel method throws an AppError. When I try to use the bluebird filtered catch, I get the following error:
Class constructor AppError cannot be invoked without 'new'
cancel()
.catch(AppError, (e) => {
// This is not working
});
But the following code will just work:
cancel()
.catch((e) => {
if (e instanceof AppError) {
// This is working
}
throw e;
});
Can someone please explain why the bluebird approach isn't working?
I have seen this post: Why doesn't instanceof work on instances of Error subclasses under babel-node? But I can't figure it out.

Categories