Custom error class in TypeScript

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?

126690 次浏览

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

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);
}

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());
}

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.

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

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.