Extended Errors do not have message or stack trace

后端 未结 2 772
时光说笑
时光说笑 2020-12-10 13:27

When running this snippet through BabelJS:

class FooError extends Error {
  constructor(message) {
    super(message);
  }
}

let error = new FooError(\'foo\         


        
相关标签:
2条回答
  • 2020-12-10 13:59

    This is a limitation due to the downlevel compilation of ES6 to ES5. Find more about this in TypeScript's explanation.

    As a workaround you can do:

    class QueryLimitError extends Error {
      __proto__: QueryLimitError;
    
      constructor(message) {
        const trueProto = new.target.prototype;
        super(message);
        this.__proto__ = trueProto;
      }
    }
    

    In some cases cases Object.setPrototypeOf(this, FooError.prototype); might be enough.

    You'll find more information in the corresponding Github issue and the

    0 讨论(0)
  • 2020-12-10 14:17

    In short, extending using babel's transpiled code only works for classes built in a specific way, and a lot of native stuff doesn't appear to be built like that. Babel's docs warns that extending many native classes doesn't work properly.

    You could create a buffer class that creates the properties "manually", something like this:

    class ErrorClass extends Error {
      constructor (message) {
        super();
    
        if (Error.hasOwnProperty('captureStackTrace'))
            Error.captureStackTrace(this, this.constructor);
        else
           Object.defineProperty(this, 'stack', {
              value: (new Error()).stack
          });
    
        Object.defineProperty(this, 'message', {
          value: message
        });
      }
    
    }
    

    Then extend that class instead:

    class FooError extends ErrorClass {
      constructor(message) {
        super(message);
      }
    }
    

    Why doesn't it work as you'd expect?

    If you look at what is transpiled, you'll see that babel first assigns a copy of the super class' prototype to the sub class, then when you call new SubClass() this function is called:

    _get(Object.getPrototypeOf(FooError.prototype), "constructor", this).call(this, message)
    

    Where _get is a helper function injected into the script:

    (function get(object, property, receiver) {
      var desc = Object.getOwnPropertyDescriptor(object, property);
    
      if (desc === undefined) {
        var parent = Object.getPrototypeOf(object);
    
        if (parent === null) {
          return undefined;
        } else {
          return get(parent, property, receiver);
        }
      } else if ("value" in desc) {
        return desc.value;
      } else {
        var getter = desc.get;
    
        if (getter === undefined) {
          return undefined;
        }
    
        return getter.call(receiver);
      }
    });
    

    it does something like finds the constructor property descriptor of the sub class' prototype's prototype and tried to call its getter with the new subclass instance as context if it exists or return its value (if ("value" in desc)), in this case the Error constructor itself. It doesn't assign anything to this from the super call so while the new object has the right prototype, it didn't get constructed the way you expect. Basically the super call does nothing to the newly constructed object, just creates a new Error which isn't assigned to anything.

    If we use the ErrorClass defined above, it does adhere to the class structure as expected by Babel.

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