Расширение ошибки в Javascript с помощью синтаксиса ES6 & Babel


Я пытаюсь расширить ошибку с ES6 и Babel. Ничего не получается.

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

var error = new Error("ll");
var myerror = new MyError("ll");
console.log(error.message) //shows up correctly
console.log(myerror.message) //shows empty string

объект ошибки никогда не получает правильный набор сообщений.

попробуйте в Babel REPL.

теперь я видел несколько решений на SO (например тут), но все они кажутся очень не-ES6-y. как это сделать в хорошем, ES6 способе? (То есть работает в Вавилоне)

11 114

11 ответов:

основываясь на ответе Карела Билека, я бы внес небольшое изменение в constructor:

class ExtendableError extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
    if (typeof Error.captureStackTrace === 'function') {
      Error.captureStackTrace(this, this.constructor);
    } else { 
      this.stack = (new Error(message)).stack; 
    }
  }
}    

// now I can extend

class MyError extends ExtendableError {}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);

выводит MyError в стеке, а не универсального Error.

Он также добавит сообщение об ошибке в трассировку стека, которая отсутствовала в Примере Карела.

Он также будет использовать captureStackTrace если он доступен.

С Бабелем 6, вам нужно transform-builtin-extend (npm) для этой работы.

объединение ответ,ответ и код, Я сделал этот небольшой вспомогательный класс, который, кажется, работает нормально.

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

попробуйте в REPL

чтобы, наконец, положить этому конец. В Babel 6 явно, что разработчики не поддерживают расширение от встроенного. Хотя это трюк не будет помощь с такими вещами, как Map,Set и т. д. это действительно работает для Error. Это важно, поскольку одна из основных идей языка, который может вызвать исключение, - разрешить пользовательские ошибки. Это вдвойне важно, поскольку обещания становятся более полезными, поскольку они предназначены для отклонить Ошибка.

печальная правда заключается в том, что вам все еще нужно выполнить это по-старому в ES2015.

пример в Babel REPL

пользовательский шаблон ошибка

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

С другой стороны, есть плагин для Babel 6, чтобы позволить это.

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

обновление: (по состоянию на 2016-09-29) после некоторых испытаний оказывается, что babel.io не учитывает должным образом все утверждения (расширение от пользовательской расширенной ошибки). Но в Эмбер.Ошибка расширения JS работает как ожидалось:https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce

Edit: нарушение изменений в Typescript 2.1

расширение встроенных модулей, таких как ошибка, массив и карта, может больше не работать.

в качестве рекомендации, вы можете вручную настроить прототип сразу после любой супер(...) телефонные переговоры.

редактирование оригинального ответа ли Бенсона немного работает для меня. Это также добавляет stack и дополнительные методы ExtendableError класс для экземпляра.

class ExtendableError extends Error {
   constructor(message) {
       super(message);
       Object.setPrototypeOf(this, ExtendableError.prototype);
       this.name = this.constructor.name;
   }

   dump() {
       return { message: this.message, stack: this.stack }
   }
 }    

class MyError extends ExtendableError {}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror.dump());
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);

С последними изменениями в babel 6, я нахожу transform-builtin-extend больше не работает. В итоге я использовал этот смешанный подход:

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;

и

import MyError from './MyError';

export default class MyChildError extends MyError {
    constructor (message) {
        super(message);
    }
}

в результате все эти тесты проходят:

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

цитирую

class MyError extends Error {
  constructor(message) {
    super(message);
    this.message = message;
    this.name = 'MyError';
  }
}

в этом нет необходимости this.stack = (new Error()).stack; трюк спасибо super() звонок.

хотя выше коды не могут вывести трассировку стека, если this.stack = (new Error()).stack; или Error.captureStackTrace(this, this.constructor.name); вызывается в Бабель. ИМО, это может быть одна проблема здесь.

на самом деле, трассировка стека может быть выход под Chrome console и Node.js v4.2.1 С этим фрагменты кода.

class MyError extends Error{
        constructor(msg) {
                super(msg);
                this.message = msg;
                this.name = 'MyError';
        }
};

var myerr = new MyError("test");
console.log(myerr.stack);
console.log(myerr);

выход Chrome console.

MyError: test
    at MyError (<anonymous>:3:28)
    at <anonymous>:12:19
    at Object.InjectedScript._evaluateOn (<anonymous>:875:140)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)
    at Object.InjectedScript.evaluate (<anonymous>:664:21)

выход Node.js

MyError: test
    at MyError (/home/bsadmin/test/test.js:5:8)
    at Object.<anonymous> (/home/bsadmin/test/test.js:11:13)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:467:10)
    at startup (node.js:134:18)
    at node.js:961:3

в дополнение к ответу @zangw вы можете определить свои ошибки следующим образом:

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

который будет бросает правильное имя, сообщение и 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

Я пытаюсь расширить ошибку с ES6

Это class MyError extends Error {…} синтаксис.

обратите внимание, что транспилеры по-прежнему имеют проблемы с наследованием от встроенных объектов. В вашем случае,

var err = super(m);
Object.assign(this, err);

кажется, чтобы решить эту проблему.

учитывая это, принятый ответ больше не работает, вы всегда можете использовать фабрику в качестве альтернативы ( 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);

как упоминает @sukima, вы не можете расширить собственный JS. На вопрос ОП ответить невозможно.

аналогично Melbourne2991 ответ, Я действительно использовал фабрику, но следовал рекомендация MDN для типов ошибок клиентов.

function extendError(className){
  function CustomError(message){
    this.name = className;
    this.message = message;
    this.stack = new Error().stack; // Optional
  }
  CustomError.prototype = Object.create(Error.prototype);
  CustomError.prototype.constructor = CustomError;
  return CustomError;
}

не используя Babel, но в простом ES6, кажется, что для меня отлично работает следующее:

class CustomError extends Error {
    constructor(...args) {
        super(...args);
        this.name = this.constructor.name;
    }
}

тестирование от REPL:

> const ce = new CustomError('foobar');
> ce.name
'CustomError'
> ce.message
'foobar'
> ce instanceof CustomError
true
> ce.stack
'CustomError: foobar\n    at CustomError (repl:3:1)\n ...'

Как вы можете видеть, стек содержит как имя ошибки, так и сообщение. Я не уверен, что что-то упускаю, но все остальные ответы, похоже, слишком усложняют ситуацию.