How do I extend a host object (e.g. Error) in Type

2020-01-31 02:30发布

I would like to extend the host object Error to a custom UploadError class. The following example fails when I compile:

class UploadError extends Error {
    constructor(message: string, private code: number) {
        super(message);
    }

    getCode(): number {
        return this.code;
    }
}

When I run the TypeScript compiler tsc I get the following error:

UploadError.ts(1,0): A export class may only extend other classes, Error is an interface.

It seems Error is defined as an interface. If anyone knows what the name of the implementation is it would make me very happy :-)

Update: I want to use Typescripts inheritance not prototypical inheritance like I currently employ to hack around this:

function UploadError (message: string, code: number) {
    this.message = message;
    this.code = code;
}

UploadError.prototype = new Error();

UploadError.prototype.constructor = UploadError;

UploadError.prototype.getCode = (): number => {
    return this.code;
}

11条回答
2楼-- · 2020-01-31 02:59

This is now possible to extend Error class version 1.6. See pull request Allow expressions in class extends clauses https://github.com/Microsoft/TypeScript/pull/3516 and issue Can not extend built in types https://github.com/Microsoft/TypeScript/issues/1168

Note that tsc won't complain anymore but your editor/IDE will until it gets updated.

查看更多
霸刀☆藐视天下
3楼-- · 2020-01-31 03:01

I know the answer has been accepted, and the solution is definitely impressive, but I really don't want that amount of code in my projects just for an exception.

Until TypeScript gets proper exceptions at the language-level somehow, with Error being so cumbersome to extend, I'm now using the following very simple solution:

class MyError {
    constructor(error: Error) {
        error.name = this['constructor'].name;
        error['type'] = this; // for type-checking in exception-handlers

        return error;
    }
}

throw new MyError(new Error('aw snap!'));

Now, my error-types are really classes - you can extend them, and you will see the correct class-name on the console when an unhandled Error is thrown; but my Error objects are not instances of those classes: the constructor does not return an instance of MyError, it just applies it's own name to the Error instance you pass to it.

This also provides a simple work-around for the issue of Error producing it's stack-trace at the point where you construct it, rather than at the point where you throw it - since the constructor signature forces you to construct a "real" Error instance.

If you need to check the type of exception in your exception-handler, grab the ['type'] property and compare it using instanceof:

try {
    // something throws new MyError(new Error('aw snap!'))
} catch (error) {
    console.log(error['type'] instanceof MyError); // => true
}

It's not ideal, but it's simple and it works.

Be aware that, if you extend MyError, you will need to implement the constructor every time and add return super(...), since the default constructor generated by TypeScript does not expect constructors that use the return-statement. It does allow them though.

查看更多
Anthone
4楼-- · 2020-01-31 03:02

You can use the prototype to add functions and attributes:

interface Error {
   code: number;
   getCode(): number;
}   

Error.prototype.code = 1;

Error.prototype.getCode = function () {
    return this.code;
}  
var myError = new Error();
console.log("Code: " + myError.getCode());

When run with node.js, this produces the following output:

Code: 1

Error is defined in lib.d.ts as follows:

interface Error {
    name: string;
    message: string;
}

declare var Error: {
    new (message?: string): Error;
    (message?: string): Error;
    prototype: Error;
}

Other than that, I see only the obvious solution to define your own interface that extends Error:

interface MyErrorInterface extends Error {       
    code: number;       
    getCode(): number;    
}       
class MyError implements MyErrorInterface {      
    code : number;      
    name : string;      
    message : string;
    constructor(code: number, name? : string, message? : string) {
        this.code = code;
        this.name = name;
        this.message = message;
    }   
    getCode(): number {
        return this.code;
    }   
}   

var myError = new MyError(1);
console.log("Code: " + myError.getCode());
查看更多
家丑人穷心不美
5楼-- · 2020-01-31 03:06

I found solution. Is not nice, but working as well... using eval() JS function to ignore TypeScript check.

declare class ErrorClass implements Error {
    public name: string;
    public message: string;
    constructor(message?: string);
}
eval('ErrorClass = Error');

export = Exception;
class Exception extends ErrorClass implements Error { ... }
查看更多
放我归山
6楼-- · 2020-01-31 03:07

pay attention on the new changes in Typescript 2.1 - link

So you can extend the Error class but, as a recommendation, you need manually adjust the prototype immediately after any super(...) calls:

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

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

    sayHello() {
        return "hello " + this.message;
    }
}
查看更多
三岁会撩人
7楼-- · 2020-01-31 03:13

I have found the following approach works:

declare class ErrorClass implements Error {
    public name: string;
    public message: string;
    constructor(message?: string);
}
var ErrorClass = Error;

class MyError extends ErrorClass {
    public name = "MyError";
    constructor (public message?: string) {
        super(message);
    }
}

The generated script looks like:

var ErrorClass = Error;
var MyError = (function (_super) {
    __extends(MyError, _super);
    function MyError(message) {
        _super.call(this, message);
        this.message = message;
        this.name = "MyError";
    }
    return MyError;
})(ErrorClass);
查看更多
登录 后发表回答