Naming Exceptions in NodeJS

In my last post I started testing my new configuration SDK. My initial code was very simple boilerplate:

/// <reference path="../typings/node/node.d.ts" />
/// <reference path="IConfigurationSource.ts" />

import path = require('path');

import { IConfigurationSource } from './IConfigurationSource';
import { InvalidOperationError, NotImplementedError } from './Exceptions';

export default class JsonConfigurationSource implements IConfigurationSource {
    private location: string;

    constructor(location: string) {
        this.location = path.resolve(location);
        throw new NotImplementedError();
    }

    get name(): string {
        return 'JSON';
    }

    set name(v: string) {
        throw new InvalidOperationError();
    }

    get id(): string {
        return `JSON-${this.location}`;
    }

    set id(v: string) {
        throw new InvalidOperationError();
    }

    toObject(): Object {
        throw new NotImplementedError();
    }
}

I have implemented a few new errors in a separate file called Exceptions.ts:

/// <reference path="../typings/node/node.d.ts" />

export class InvalidOperationError extends Error {
    constructor() {
        super('Invalid Operation');
    }
}

export class NotImplementedError extends Error {
    constructor() {
        super('Not Implemented');
    }
}

The theory being that I can tell the difference between something that isn’t implemented and something that is broken. They both inherit from the standard NodeJS Error class. So far so good. What’s the problem? Well, it’s in the tests:

10022015-1

That assertion error isn’t really helpful. Basically, “error – we were expecting an error, but we got an error”. That’s because I have not renamed the error object. There are three properties on the Error object: message, name and stack. I am setting the message property when I call the super() in the constructor. What I am seeing in the display is the name property.

Note that I can still tell the difference between an InvalidOperationError and a NotImplementedError (and any other sort of error) by using the instanceof operator. This is about display only and the only place this really matters is in testing and log files.

The solution to this is relatively simple – set a name:

/// <reference path="../typings/node/node.d.ts" />

export class InvalidOperationError extends Error {
    constructor() {
        super('Invalid Operation');
    }

    name: string = 'InvalidOperationError';
}

export class NotImplementedError extends Error {
    constructor() {
        super('Not Implemented');
    }

    name: string = 'NotImplementedError';
}

Once this is in place, the failed tests now show the proper error:

10022015-2

The big take away here is to understand the classes you are sub-classing. Make sure you set the proper properties to provide appropriate debug messages.