Расширение ошибки в Javascript с синтаксисом ES6 и Babel

Я пытаюсь расширить Error с помощью 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

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

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

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

Ответ 1

Основываясь на ответе Карела Билек, я внес бы небольшое изменение в 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.

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

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

С Babel 6 вам нужно transform-builtin-extend (npm), чтобы это работало.

Ответ 2

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

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

Ответ 3

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

Печальная истина заключается в том, что вам все равно нужно выполнить это по-старому в 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 не учитывает должным образом все утверждения (простирающиеся от пользовательской расширенной ошибки). Но в Ember.JS расширение Error работает как ожидалось: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce

Ответ 4

Изменить: Нарушение изменений в TypScript 2.1

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

В качестве рекомендации вы можете вручную настроить прототип сразу после любых вызовов super (...).

Редактирование оригинального ответа Ли Бенсона немного для меня. Это также добавляет 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 {
    constructor(message) {
        super(message);
        Object.setPrototypeOf(this, MyError.prototype);
    }
}

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

Ответ 5

С последними изменениями в 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');

Ответ 6

Цитата

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); не вызывается в Babel. ИМО, это может быть одна проблема здесь.

Собственно, трассировка стека может выводиться в 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

Ответ 7

В дополнение к ответу @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');

который выдает правильное имя, сообщение и стек:

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

Ответ 8

Я пытаюсь расширить Error с ES6

Правильный синтаксис class MyError extends Error {…}.

Обратите внимание, что у transpilers все еще есть проблемы с наследованием от встроенных объектов. В вашем случае

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

похоже, устраняет проблему.

Ответ 9

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

Ответ 10

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

Как и ответ Melbourne2991, я использовал скорее factory, а затем 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;
}

Ответ 11

Не используя 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 ...'

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