Typescript - Extending Error class

前端 未结 6 926
一向
一向 2020-11-27 03:07

I\'m trying to throw a custom error with my \"CustomError\" class name printed in the console instead of \"Error\", with no success:

class CustomError extend         


        
相关标签:
6条回答
  • 2020-11-27 03:31

    Are you using typescript version 2.1, and transpiling to ES5? Check this section of the breaking changes page for possible issues and workaround: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work

    The relevant bit:

    As a recommendation, you can 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;
        }
    }
    

    However, any subclass of FooError will have to manually set the prototype as well. For runtimes that don't support Object.setPrototypeOf, you may instead be able to use __proto__.

    Unfortunately, these workarounds will not work on Internet Explorer 10 and prior. One can manually copy methods from the prototype onto the instance itself (i.e. FooError.prototype onto this), but the prototype chain itself cannot be fixed.

    0 讨论(0)
  • 2020-11-27 03:34

    Try this...

    class CustomError extends Error { 
    
      constructor(message: string) {
        super(`Lorem "${message}" ipsum dolor.`)
      }
    
      get name() { return this.constructor.name }
    
    }
    
    throw new CustomError('foo')
    
    0 讨论(0)
  • 2020-11-27 03:38

    As of TypeScript 2.2 it can be done via new.target.prototype. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#example

    class CustomError extends Error {
        constructor(message?: string) {
            super(message); // 'Error' breaks prototype chain here
            this.name = 'CustomError';
            Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
        }
    }
    
    0 讨论(0)
  • 2020-11-27 03:39

    I ran into the same problem in my typescript project a few days ago. To make it work, I use the implementation from MDN using only vanilla js. So your error would look something like the following:

    function CustomError(message) {
      this.name = 'CustomError';
      this.message = message || 'Default Message';
      this.stack = (new Error()).stack;
    }
    CustomError.prototype = Object.create(Error.prototype);
    CustomError.prototype.constructor = CustomError;
    
    throw new CustomError('foo');

    It doesn't seem to work in SO code snippet, but it does in the chrome console and in my typescript project:

    0 讨论(0)
  • 2020-11-27 03:45

    The problem is that Javascript's built-in class Error breaks the prototype chain by switching the object to be constructed (i.e. this) to a new, different object, when you call super and that new object doesn't have the expected prototype chain, i.e. it's an instance of Error not of CustomError.

    This problem can be elegantly solved using 'new.target', which is supported since Typescript 2.2, see here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html

    class CustomError extends Error {
      constructor(message?: string) {
        // 'Error' breaks prototype chain here
        super(message); 
    
        // restore prototype chain   
        const actualProto = new.target.prototype;
    
        if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); } 
        else { this.__proto__ = actualProto; } 
      }
    }
    

    Using new.target has the advantage that you don't have to hardcode the prototype, like some other answers here proposed. That again has the advantage that classes inheriting from CustomError will automatically also get the correct prototype chain.

    If you were to hardcode the prototype (e.g. Object.setPrototype(this, CustomError.prototype)), CustomError itself would have a working prototype chain, but any classes inheriting from CustomError would be broken, e.g. instances of a class VeryCustomError < CustomError would not be instanceof VeryCustomError as expected, but only instanceof CustomError.

    See also: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200

    0 讨论(0)
  • 2020-11-27 03:45

    It works correctly in ES2015 (https://jsfiddle.net/x40n2gyr/). Most likely, the problem is that the TypeScript compiler is transpiling to ES5, and Error cannot be correctly subclassed using only ES5 features; it can only be correctly subclassed using ES2015 and above features (class or, more obscurely, Reflect.construct). This is because when you call Error as a function (rather than via new or, in ES2015, super or Reflect.construct), it ignores this and creates a new Error.

    You'll probably have to live with the imperfect output until you can target ES2015 or higher...

    0 讨论(0)
提交回复
热议问题