Extending Error in Javascript with ES6 syntax & Babel

后端 未结 14 1757
你的背包
你的背包 2020-11-28 01:28

I am trying to extend Error with ES6 and Babel. It isn\'t working out.

class MyError extends Error {
  constructor(m) {
    super(m);
  }
}

var error = new          


        
相关标签:
14条回答
  • 2020-11-28 01:51

    With the latest changes in babel 6, I find transform-builtin-extend no longer working. I ended up using this mixed approach:

    export default class MyError {
        constructor (message) {
            this.name = this.constructor.name;
            this.message = message;
            this.stack = (new Error(message)).stack;
        }
    }
    
    MyError.prototype = Object.create(Error.prototype);
    MyError.prototype.constructor = MyError;
    

    and

    import MyError from './MyError';
    
    export default class MyChildError extends MyError {
        constructor (message) {
            super(message);
        }
    }
    

    As a result all these tests pass:

    const sut = new MyError('error message');
    expect(sut.message).toBe('error message');
    expect(sut).toBeInstanceOf(Error);
    expect(sut).toBeInstanceOf(MyError);
    expect(sut.name).toBe('MyError');
    expect(typeof sut.stack).toBe('string');
    
    const sut = new MyChildError('error message');
    expect(sut.message).toBe('error message');
    expect(sut).toBeInstanceOf(Error);
    expect(sut).toBeInstanceOf(MyError);
    expect(sut).toBeInstanceOf(MyChildError);
    expect(sut.name).toBe('MyChildError');
    expect(typeof sut.stack).toBe('string');
    
    0 讨论(0)
  • 2020-11-28 01:52

    To finally put this to rest. In Babel 6 it is explicit that the developers do not support extending from built in. Although this trick will not help with things like Map, Set, etc. it does work for Error. This is important as one of the core ideas of a language that can throw an exception is to allow custom Errors. This is doubly important as Promises become more useful since they to are designed to reject an Error.

    The sad truth is you still need to perform this the old way in ES2015.

    Example in Babel REPL

    Custom Error pattern

    class MyError {
      constructor(message) {
        this.name = 'MyError';
        this.message = message;
        this.stack = new Error().stack; // Optional
      }
    }
    MyError.prototype = Object.create(Error.prototype);
    

    On the other hand there is a plugin for Babel 6 to allow this.

    https://www.npmjs.com/package/babel-plugin-transform-builtin-extend

    Update: (as of 2016-09-29) After some testing it appears that babel.io does not properly account for all the asserts (extending from a custom extended Error). But in Ember.JS extending Error works as expected: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce

    0 讨论(0)
  • 2020-11-28 01:52

    I prefer more strong syntax than described above. Additional methods at error type will help you to create pretty console.log or something else.

    export class CustomError extends Error {
        /**
         * @param {string} message
         * @param {number} [code = 0]
         */
        constructor(message, code = 0) {
            super();
    
            /**
             * @type {string}
             * @readonly
             */
            this.message = message;
    
            /**
             * @type {number}
             * @readonly
             */
            this.code = code;
    
            /**
             * @type {string}
             * @readonly
             */
            this.name = this.constructor.name;
    
            /**
             * @type {string}
             * @readonly
             */
            this.stack = CustomError.createStack(this);
        }
    
        /**
         * @return {string}
         */
        toString() {
            return this.getPrettyMessage();
        }
    
        /**
         * @return {string}
         */
        getPrettyMessage() {
            return `${this.message} Code: ${this.code}.`;
        }
    
        /**
         * @param {CustomError} error
         * @return {string}
         * @private
         */
        static createStack(error) {
            return typeof Error.captureStackTrace === 'function'
                ? Error.captureStackTrace(error, error.constructor)
                : (new Error()).stack;
        }
    }
    

    To test this code you can run something similar:

    try {
        throw new CustomError('Custom error was thrown!');
    } catch (e) {
        const message = e.getPrettyMessage();
    
        console.warn(message);
    }
    

    Extending of CustomError type are welcome. It is possible to add some specific functionality to the extended type or override existing. For example.

    export class RequestError extends CustomError {
        /**
         * @param {string} message
         * @param {string} requestUrl
         * @param {number} [code = 0]
         */
        constructor(message, requestUrl, code = 0) {
            super(message, code);
    
            /**
             * @type {string}
             * @readonly
             */
            this.requestUrl = requestUrl;
        }
    
        /**
         * @return {string}
         */
        getPrettyMessage() {
            const base = super.getPrettyMessage();
    
            return `${base} Request URL: ${this.requestUrl}.`;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 01:53

    Given this the accepted answer no longer works you could always use a factory as an alternative (repl):

    function ErrorFactory(name) {
       return class AppError extends Error {
        constructor(message) {
          super(message);
          this.name = name;
          this.message = message; 
          if (typeof Error.captureStackTrace === 'function') {
            Error.captureStackTrace(this, this.constructor);
          } else { 
            this.stack = (new Error(message)).stack; 
          }
        }
      }     
    }
    
    // now I can extend
    const MyError = ErrorFactory("MyError");
    
    
    var myerror = new MyError("ll");
    console.log(myerror.message);
    console.log(myerror instanceof Error);
    console.log(myerror.name);
    console.log(myerror.stack);

    0 讨论(0)
  • 2020-11-28 01:54

    Combining this answer, this answer and this code, I have made this small "helper" class, that seems to work fine.

    class ExtendableError extends Error {
      constructor(message) {
        super();
        this.message = message; 
        this.stack = (new Error()).stack;
        this.name = this.constructor.name;
      }
    }    
    
    // now I can extend
    
    class MyError extends ExtendableError {
      constructor(m) {   
        super(m);
      }
    }
    
    var myerror = new MyError("ll");
    console.log(myerror.message);
    console.log(myerror instanceof Error);
    console.log(myerror.name);
    console.log(myerror.stack);
    

    Try in REPL

    0 讨论(0)
  • 2020-11-28 01:56

    In addition to @zangw answer, you can define your errors like this:

    'use strict';
    
    class UserError extends Error {
      constructor(msg) {
        super(msg);
        this.name = this.constructor.name;
      }
    }
    
    // define errors
    class MyError extends UserError {}
    class MyOtherError extends UserError {}
    
    console.log(new MyError instanceof Error); // true
    
    throw new MyError('My message');
    

    which will throws correct name, message and stacktrace:

    MyError: My message
        at UserError (/Users/honzicek/Projects/api/temp.js:5:10)
        at MyError (/Users/honzicek/Projects/api/temp.js:10:1)
        at Object.<anonymous> (/Users/honzicek/Projects/api/temp.js:14:7)
        at Module._compile (module.js:434:26)
        at Object.Module._extensions..js (module.js:452:10)
        at Module.load (module.js:355:32)
        at Function.Module._load (module.js:310:12)
        at Function.Module.runMain (module.js:475:10)
        at startup (node.js:117:18)
        at node.js:951:3
    
    0 讨论(0)
提交回复
热议问题