140

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?

Community
  • 1
  • 1
Kuba T
  • 2,893
  • 4
  • 25
  • 30
  • 1
    In what way do those answers not help you? You can't extend the Error class yet. [That's coming in 1.6.](https://github.com/Microsoft/TypeScript/pull/3516) – David Sherret Jul 25 '15 at 14:45
  • @DavidSherret I have some compilation errors which, as I see, haven't been reported by `tsc` in earlier versions. – Kuba T Jul 25 '15 at 19:09

6 Answers6

214

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());
}
Pelfox
  • 3
  • 1
  • 2
ziv
  • 3,641
  • 3
  • 21
  • 26
31

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
);
thoughtrepo
  • 8,306
  • 2
  • 27
  • 21
  • 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! :) – Kuba T Jul 25 '15 at 19:10
  • BaseError cannot define methods with class syntax, this way they will be replaced with `BaseError.prototype = new Error()`. – Estus Flask Apr 22 '18 at 14:26
22

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);
}
Benny Code
  • 51,456
  • 28
  • 233
  • 198
  • 2
    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." – StewartArmbrecht Aug 02 '17 at 09:59
  • 8
    I don't understand why this is necessary in the first place: `this.stack = ( new Error()).stack;` Should be inherited from the Error class, yes? – DarkNeuron Jan 23 '18 at 14:12
19

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.

Peter
  • 4,493
  • 6
  • 41
  • 64
  • 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. – Johnathon Sullinger Aug 16 '21 at 01:35
12

This does not appear to be an issue in the current latest version of typescript (4.7.3):

import { expect } from 'chai';
import { describe } from 'mocha';

class MyError extends Error {}

describe('Custom error class "MyError"', () => {
  it('should be an instance of "MyError"', () => {
    try {
      throw new MyError();
    } catch (e) {
      expect(e).to.be.an.instanceOf(MyError);
    }
  });
});

I am unsure what version changed the behaviour but this test fails on 2.1.

j1n3l0
  • 517
  • 4
  • 16
  • I concur, similar test works for me on Node 18 and Typescript 4.9. I also could not find the issue or changelog where this behaviour started working again, only the original announcement that it would break back in TS 2.2. – Anentropic Feb 17 '23 at 10:26
  • The code certainly seems to work fine without the `Object.setPrototypeOf` call. It would be great though if someone could find an official reference regarding when and how this became obsolete. – Daniel Wolf Apr 04 '23 at 08:25
10

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')
Hugo Gresse
  • 17,195
  • 9
  • 77
  • 119