Custom error class in TypeScript

93,418

Solution 1

TypeScript 2.1 had a breaking changes regarding Extending built-ins like Error.

From the TypeScript breaking changes documentation

class FooError extends Error {
    constructor(msg: string) {
        super(msg);

        // Set the prototype explicitly.
        Object.setPrototypeOf(this, FooError.prototype);
    }

    sayHello() {
        return "hello " + this.message;
    }
}

Then you can use:

let error = new FooError("Something really bad went wrong");
if(error instanceof FooError){
   console.log(error.sayHello());
}

Solution 2

Until 1.6 rolls around, I've just been making my own extendable classes.

class BaseError {
    constructor () {
        Error.apply(this, arguments);
    }
}

BaseError.prototype = new Error();

class HttpRequestError extends BaseError {
    constructor (public status: number, public message: string) {
        super();    
    }
}

var error = new HttpRequestError(500, 'Server Error');

console.log(
    error,
    // True
    error instanceof HttpRequestError,
    // True
    error instanceof Error
);

Solution 3

I am using TypeScript 1.8 and this is how I use custom error classes:

UnexpectedInput.ts

class UnexpectedInput extends Error {

  public static UNSUPPORTED_TYPE: string = "Please provide a 'String', 'Uint8Array' or 'Array'.";

  constructor(public message?: string) {
    super(message);
    this.name = "UnexpectedInput";
    this.stack = (<any> new Error()).stack;
  }

}

export default UnexpectedInput;

MyApp.ts

import UnexpectedInput from "./UnexpectedInput";

...

throw new UnexpectedInput(UnexpectedInput.UNSUPPORTED_TYPE);

For TypeScript versions older than 1.8, you need to declare Error:

export declare class Error {
  public message: string;
  public name: string;
  public stack: string;
  constructor(message?: string);
}

Solution 4

For Typescript 3.7.5 this code provided a custom error class that also captured the correct stack information. Note instanceof does not work so I use name instead

// based on https://gunargessner.com/subclassing-exception

// example usage
try {
  throw new DataError('Boom')
} catch(error) {
  console.log(error.name === 'DataError') // true
  console.log(error instanceof DataError) // false
  console.log(error instanceof Error) // true
}

class DataError {
  constructor(message: string) {
    const error = Error(message);

    // set immutable object properties
    Object.defineProperty(error, 'message', {
      get() {
        return message;
      }
    });
    Object.defineProperty(error, 'name', {
      get() {
        return 'DataError';
      }
    });
    // capture where error occured
    Error.captureStackTrace(error, DataError);
    return error;
  }
}

There are some other alternatives and a discussion of the reasons.

Solution 5

There is a neat library for this at https://www.npmjs.com/package/ts-custom-error

ts-custom-error allow you to create error custom Error very easily:

import { CustomError } from 'ts-custom-error'
 
class HttpError extends CustomError {
    public constructor(
        public code: number,
        message?: string,
    ) {
        super(message)
    }
}

usage:

new HttpError(404, 'Not found')
Share:
93,418
Kuba T
Author by

Kuba T

Updated on June 24, 2021

Comments

  • Kuba T
    Kuba T about 3 years

    I'd like to create my own error class in TypeScript, extending core Error to provide better error handling and customized reporting. For example, I want to create an HttpRequestError class with url, response and body passed into its constructor, which reponds with Http request to http://example.com failed with status code 500 and message: Something went wrong and proper stack trace.

    How to extend core Error class in TypeScript? I've already found post in SO: How do I extend a host object (e.g. Error) in TypeScript but this solution doesn't work for me. I use TypeScript 1.5.3

    Any ideas?

  • Kuba T
    Kuba T almost 9 years
    Yeah, my solutions are familiar, the only thing I wondered about is how to extend core classes in the same way as project's ones. Saddly, I don't see any date when TS 1.6 could be released. So, well, I think your solution is the closest to my expectations so far, thanks! :)
  • Benny Neugebauer
    Benny Neugebauer over 7 years
    It's worth mentioning that Object.setPrototypeOf needs to be called immediately after any super(...) calls.
  • Searene
    Searene over 7 years
    Why do we need to add Object.setPrototypeOf(this, FooError.prototype);?
  • Westy92
    Westy92 over 7 years
    I needed to make sure tsconfig.json had "target": "es6".
  • StewartArmbrecht
    StewartArmbrecht almost 7 years
    You should be careful with this approach. I thought I read that calling the stack property is EXPENSIVE and should be avoided in your code. I think you might be adding a significant amount of overhead for you custom errors. From the documentation "The string representing the stack trace is lazily generated when the error.stack property is accessed."
  • DarkNeuron
    DarkNeuron over 6 years
    I don't understand why this is necessary in the first place: this.stack = (<any> new Error()).stack; Should be inherited from the Error class, yes?
  • Estus Flask
    Estus Flask about 6 years
    BaseError cannot define methods with class syntax, this way they will be replaced with BaseError.prototype = new Error().
  • Teoman shipahi
    Teoman shipahi over 5 years
  • ChetPrickles
    ChetPrickles over 3 years
    @Searene If you click the "TypeScript breaking changes documentation" link it explains why.
  • Johnathon Sullinger
    Johnathon Sullinger almost 3 years
    You can get the name without having to define a property on it with the name explicitly hard-coded via error.constructor.name. This will return the Type name for you.