Extension de L'erreur en Javascript avec la syntaxe ES6 et Babel

J'essaie d'étendre L'erreur avec ES6 et Babel. Ce n'est pas le travail.

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

L'objet D'erreur n'obtient jamais le bon jeu de messages.

Essayez dans Babel REPL .

Maintenant, j'ai vu quelques solutions sur SO ( par exemple ici), mais elles semblent toutes très un-ES6-Y. comment le faire d'une manière agréable, ES6? (Cela fonctionne dans Babel)

114
demandé sur Indolering 2015-06-27 17:28:27

11 réponses

Basé sur la réponse DE Karel Bílek, je ferais un petit changement à la 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);

Cela imprimera MyError dans la pile, et non le générique Error.

Il ajoutera également le message d'erreur à la trace de la pile-qui manquait dans L'exemple de Karel.

Il utilisera également captureStackTrace s'il est disponible.

Avec Babel 6, vous avez besoin d' transformer-builtin-étendre (npm) pour que cela fonctionne.

159
répondu Lee Benson 2017-01-20 14:59:09

Combinant cette réponse, cette réponse et ce code, j'ai fait ce petit "aide" de la classe, qui semble bien fonctionner.

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

Essayez dans REPL

36
répondu Karel Bílek 2018-03-31 11:37:35

Pour finalement mettre cela au repos. Dans Babel 6, il est explicite que les développeurs ne supportent pas s'étendant à partir de built in. Bien que cette astuce ne aider avec des choses comme Map, Set, etc. cela fonctionne pour Error. Ceci est important car l'une des idées fondamentales d'un langage qui peut lancer une exception est d'autoriser des erreurs personnalisées. Ceci est doublement important car les promesses deviennent plus utiles car elles sont conçues pour rejeter une erreur .

La triste vérité est vous encore besoin d'effectuer cela à l'ancienne dans ES2015.

Exemple dans Babel REPL

Modèle D'erreur personnalisé

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

D'autre part, il existe un plugin pour Babel 6 pour permettre cela.

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

Mise à jour: (à partir de 2016-09-29) après quelques tests, il semble que babel.io ne tient pas correctement compte de toutes les affirmations (s'étendant à partir d'une erreur étendue personnalisée). Mais dans Braise.L'erreur d'extension js fonctionne comme prévu: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce

23
répondu Sukima 2016-09-30 02:28:43

Edit: dernières modifications de Tapuscrit 2.1

L'extension des éléments intégrés tels que Error, Array et Map peut ne plus fonctionner.

En tant que recommandation, vous pouvez ajuster manuellement le prototype immédiatement après tout super(...) appeler.

L'édition de la réponse originale de Lee Benson fonctionne un peu pour moi. Cela ajoute également stack et des méthodes supplémentaires de la classe ExtendableError à l'instance.

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);
10
répondu Artur Aleksanyan 2017-07-05 10:06:53

, Avec les derniers changements de babel 6, je trouve transformer-builtin-étendre ne fonctionne plus. J'ai fini par utiliser cette approche mixte:

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;

Et

import MyError from './MyError';

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

En conséquence, tous ces tests passent:

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
répondu Diego Ferri 2017-12-06 15:12:16

Citant

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

Il N'y a pas besoin de this.stack = (new Error()).stack; truc grâce à super() appel.

Bien que les codes ci-dessus ne puissent pas afficher la trace de la pile à moins que this.stack = (new Error()).stack; ou Error.captureStackTrace(this, this.constructor.name); ne soient invoqués dans Babel. OMI, c'est peut-être un problème ici.

En fait, la trace de pile peut être sortie sous Chrome console et Node.js v4.2.1 avec ces extraits de code.

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

Sortie de 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)

Sortie de 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
4
répondu zangw 2015-11-24 09:30:21

En plus de la réponse @zangw, vous pouvez définir vos erreurs comme ceci:

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

Qui lancera le nom, le message et la stacktrace corrects:

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
4
répondu Honza Stepanovsky 2016-02-04 14:08:58

J'essaie d'étendre L'erreur avec ES6

Cette syntaxe class MyError extends Error {…} est correcte.

Notez que les transpilers ont toujours des problèmes avec l'héritage des objets intégrés. Dans votre cas,

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

Semble résoudre le problème.

2
répondu Bergi 2015-06-27 14:38:11

Compte tenu de cela, la réponse acceptée ne fonctionne plus, vous pouvez toujours utiliser une usine comme 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);
2
répondu Melbourne2991 2016-10-08 05:35:36

Comme le mentionne @sukima, vous ne pouvez pas étendre js natif. La question du PO ne peut pas être répondue.

Similaire à la réponse de Melbourne2991 , j'ai plutôt utilisé une usine, mais j'ai suivi la recommandation de MDN pour les types d'erreur client .

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;
}
1
répondu Eric H. 2017-05-23 10:31:12

N'utilisant pas Babel, mais en clair ES6, ce qui suit semble bien fonctionner pour moi:

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

Test de 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 ...'

Comme vous pouvez le voir, la pile contient à la fois le nom de l'erreur et le message. Je ne suis pas sûr de manquer quelque chose, mais toutes les autres réponses semblent trop compliquer les choses.

0
répondu JHH 2018-04-19 07:34:46