web-dev-qa-db-fra.com

Quel est le bon moyen d'étendre Error in JavaScript?

Je veux ajouter des éléments dans mon code JS et je veux qu’ils soient des exemples d’erreur, mais je veux aussi qu’ils soient quelque chose d’autre.

En Python, en général, on sous-classe Exception. 

Quelle est la bonne chose à faire dans JS?

313
Josh Gibson

Le seul objet d'erreur de champ standard est la propriété message. (Voir MDN ou Spécification du langage EcmaScript, section 15.11) Tout le reste est spécifique à la plate-forme.

Les environnements Mosts définissent la propriété stack, mais fileName et lineNumber sont pratiquement inutiles pour être utilisés en héritage.

Ainsi, l'approche minimaliste est la suivante:

function MyError(message) {
    this.name = 'MyError';
    this.message = message;
    this.stack = (new Error()).stack;
}
MyError.prototype = new Error;  // <-- remove this if you do not 
                                //     want MyError to be instanceof Error

Vous pouvez détecter la pile, en décaler les éléments indésirables et extraire des informations telles que fileName et lineNumber, mais cela nécessite des informations sur la plate-forme sur laquelle JavaScript est en cours d'exécution. La plupart des cas sont inutiles - et vous pouvez le faire en post-mortem si vous le souhaitez vraiment.

Safari est une exception notable. Il n'y a pas de propriété stack, mais le mot clé throw définit les propriétés sourceURL et line de l'objet qui est généré. Ces choses sont garanties d'être correctes.

Les cas de test que j'ai utilisés peuvent être trouvés ici: JavaScript auto-fabriqué Erreur comparaison d'objets .

181
Tero

En ES6:

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

la source

121
Mohsen

Edit: S'il vous plaît lire les commentaires. Il s'avère que cela ne fonctionne que dans V8 (Chrome/Node.JS). Mon intention était de fournir une solution inter-navigateurs, qui fonctionnerait dans tous les navigateurs, et de fournir une trace de la pile là où l'assistance est disponible.

Edit: J'ai créé ce wiki de communauté pour permettre plus de modifications.

La solution pour V8 (Chrome/Node.JS) fonctionne dans Firefox et peut être modifiée pour fonctionner principalement correctement dans IE. (voir fin de post)

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
  Error.call(this) // Does not seem necessary. Perhaps remove this line?
  Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
  this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
  this.message = message; // Used to set the message
}

Article original sur "Montre-moi le code!"

Version courte:

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype
  Error.captureStackTrace(this, this.constructor)
  this.name = this.constructor.name
  this.message = message
}

Je garde this.constructor.prototype.__proto__ = Error.prototype dans la fonction pour garder tout le code ensemble. Mais vous pouvez également remplacer this.constructor par UserError et cela vous permet de déplacer le code en dehors de la fonction, de sorte qu'il ne soit appelé qu'une fois.

Si vous choisissez cette voie, assurez-vous d'appeler cette ligne avant la première fois que vous lancez UserError.

Cette mise en garde ne s'applique pas à la fonction, car les fonctions sont créées en premier, peu importe l'ordre. Ainsi, vous pouvez déplacer la fonction à la fin du fichier, sans problème.

Compatibilité du navigateur

Fonctionne dans Firefox et Chrome (et Node.JS) et remplit toutes les promesses.

Internet Explorer échoue dans les cas suivants

  • Les erreurs n'ont pas err.stack pour commencer, donc "ce n'est pas de ma faute".

  • Error.captureStackTrace(this, this.constructor) n'existe pas, vous devez donc faire autre chose, par exemple

    if(Error.captureStackTrace) // AKA if not IE
        Error.captureStackTrace(this, this.constructor)
    
  • toString cesse d'exister lorsque vous sous-classe Error. Donc, vous devez également ajouter.

    else
        this.toString = function () { return this.name + ': ' + this.message }
    
  • Internet Explorer ne considérera pas UserError comme un instanceof Error à moins que vous n'exécutiez ce qui suit avant le throw UserError

    UserError.prototype = Error.prototype
    
41
George Bailey

En bref:

  • Si vous utilisez ES6 sans transpilers:

    class CustomError extends Error { /* ... */}
    
  • Si vous utilisez le transpiler Babel:

Option 1: utilisez babel-plugin-transform-builtin-extend

Option 2: faites-le vous-même (inspiré de cette même bibliothèque)

    function CustomError(...args) {
      const instance = Reflect.construct(Error, args);
      Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
      return instance;
    }
    CustomError.prototype = Object.create(Error.prototype, {
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    Reflect.setPrototypeOf(CustomError, Error);
  • Si vous utilisez pure ES5:

    function CustomError(message, fileName, lineNumber) {
      var instance = new Error(message, fileName, lineNumber);
      Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
      return instance;
    }
    CustomError.prototype = Object.create(Error.prototype, {
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    if (Object.setPrototypeOf){
        Object.setPrototypeOf(CustomError, Error);
    } else {
        CustomError.__proto__ = Error;
    }
    
  • Alternative: utilisez le cadre Classtrophobic

Explication:

Pourquoi l'extension de la classe Error avec ES6 et Babel pose-t-elle un problème?

Car une instance de CustomError n'est plus reconnue en tant que telle.

class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false

En fait, dans la documentation officielle de Babel, vous ne pouvez pas étendre les classes JavaScript intégrées tels que Date, Array, DOM ou Error.

Le problème est décrit ici:

Qu'en est-il des autres SO réponses?

Toutes les réponses données résolvent le problème instanceof mais vous perdez l’erreur habituelle console.log:

console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}

Alors que vous utilisez la méthode mentionnée ci-dessus, non seulement vous corrigez le problème instanceof mais vous conservez également l'erreur habituelle console.log:

console.log(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5
31
JBE

Pour éviter le passe-partout pour chaque type d'erreur différent, j'ai combiné la sagesse de certaines des solutions dans une fonction createErrorType:

function createErrorType(name, init) {
  function E(message) {
    if (!Error.captureStackTrace)
      this.stack = (new Error()).stack;
    else
      Error.captureStackTrace(this, this.constructor);
    this.message = message;
    init && init.apply(this, arguments);
  }
  E.prototype = new Error();
  E.prototype.name = name;
  E.prototype.constructor = E;
  return E;
}

Ensuite, vous pouvez définir facilement de nouveaux types d’erreur comme suit:

var NameError = createErrorType('NameError', function (name, invalidChar) {
  this.message = 'The name ' + name + ' may not contain ' + invalidChar;
});

var UnboundError = createErrorType('UnboundError', function (variableName) {
  this.message = 'Variable ' + variableName + ' is not bound';
});
26
Ruben Verborgh

En 2018, je pense que c'est la meilleure façon; qui prend en charge IE9 + et les navigateurs modernes.

UPDATE: Voir this test et repo pour une comparaison sur différentes implémentations.

function CustomError(message) {
    Object.defineProperty(this, 'name', {
        enumerable: false,
        writable: false,
        value: 'CustomError'
    });

    Object.defineProperty(this, 'message', {
        enumerable: false,
        writable: true,
        value: message
    });

    if (Error.hasOwnProperty('captureStackTrace')) { // V8
        Error.captureStackTrace(this, CustomError);
    } else {
        Object.defineProperty(this, 'stack', {
            enumerable: false,
            writable: false,
            value: (new Error(message)).stack
        });
    }
}

if (typeof Object.setPrototypeOf === 'function') {
    Object.setPrototypeOf(CustomError.prototype, Error.prototype);
} else {
    CustomError.prototype = Object.create(Error.prototype, {
        constructor: { value: CustomError }
    });
}

Notez également que la propriété __proto__ est deprecated qui est largement utilisée dans d'autres réponses.

22
Onur Yıldırım

Par souci d’exhaustivité - du fait qu’aucune des réponses précédentes n’a mentionné cette méthode - si vous travaillez avec Node.js et n’avez pas à vous soucier de la compatibilité du navigateur, l’effet souhaité est assez facile à obtenir avec la inherits du module util ( documents officiels ici ).

Par exemple, supposons que vous souhaitiez créer une classe d'erreur personnalisée qui prend un code d'erreur en premier argument et le message d'erreur en deuxième argument:

fichier custom-error.js:

'use strict';

var util = require('util');

function CustomError(code, message) {
  Error.captureStackTrace(this, CustomError);
  this.name = CustomError.name;
  this.code = code;
  this.message = message;
}

util.inherits(CustomError, Error);

module.exports = CustomError;

Maintenant, vous pouvez instancier et passer/jeter votre CustomError:

var CustomError = require('./path/to/custom-error');

// pass as the first argument to your callback
callback(new CustomError(404, 'Not found!'));

// or, if you are working with try/catch, throw it
throw new CustomError(500, 'Server Error!');

Notez qu'avec cet extrait, la trace de la pile aura le nom de fichier et la ligne corrects, et l'instance d'erreur aura le nom correct!

Cela est dû à l'utilisation de la méthode captureStackTrace, qui crée une propriété stack sur l'objet cible (dans ce cas, la CustomError étant instanciée). Pour plus de détails sur son fonctionnement, consultez la documentation ici

18
Victor Schröder

La réponse fortement votée de Crescent Fresh est trompeuse. Bien que ses avertissements ne soient pas valides, il n’adresse pas d’autres limitations.

Premièrement, le raisonnement du paragraphe "Caveats:" de Crescent n'a pas de sens. L'explication implique que coder "un groupe de if (erreur instance de MyError) else ..." est en quelque sorte fastidieux ou prolixe par rapport à plusieurs instructions catch. Plusieurs instances de plusieurs instances dans un même bloc catch sont aussi concises que plusieurs instructions catch: code propre et concis sans astuces. C’est un excellent moyen d’imiter la gestion des erreurs par Java, spécifique aux sous-types de jetables.

WRT "apparaît, la propriété de message de la sous-classe n'est pas définie", ce n'est pas le cas si vous utilisez une sous-classe Error correctement construite. Pour créer votre propre sous-classe ErrorX Error, copiez simplement le bloc de code commençant par "var MyError =", en remplaçant le mot "MyError" par "ErrorX". (Si vous souhaitez ajouter des méthodes personnalisées à votre sous-classe, suivez le texte exemple).

La limitation réelle et significative des sous-classes d’erreur JavaScript est que, pour les implémentations JavaScript ou les débogueurs qui suivent et génèrent un suivi de la pile et de l’emplacement d’instanciation, comme FireFox, un emplacement de votre propre implémentation de sous-classe Error classe, alors que si vous utilisiez une erreur directe, ce serait l'emplacement où vous avez exécuté "nouvelle erreur (...)"). Les utilisateurs de IE ne le remarqueraient probablement jamais, mais les utilisateurs de Fire Bug sur FF verront les valeurs de nom de fichier et de numéro de ligne inutiles signalées à côté de ces erreurs, et devront explorer la trace de la pile jusqu'à l'élément # 1 pour trouver le réel. lieu d'instanciation.

18
Blaine

Que diriez-vous de cette solution?

Au lieu de lancer votre erreur personnalisée en utilisant:

throw new MyError("Oops!");

Vous devriez envelopper l'objet Error (un peu comme un décorateur):

throw new MyError(Error("Oops!"));

Cela garantit que tous les attributs sont corrects, tels que la pile, nomFichier, numéroDeLigne, et cetera.

Tout ce que vous avez à faire est alors de copier les attributs, ou de définir des getters pour eux.

function MyError(wrapped)
{
        this.wrapped = wrapped;
        this.wrapped.name = 'MyError';
}

function wrap(attr)
{
        Object.defineProperty(MyError.prototype, attr, {
                get: function()
                {
                        return this.wrapped[attr];
                }
        });
}

MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');

MyError.prototype.toString = function()
{
        return this.wrapped.toString();
};
11
JoWie

Ma solution est plus simple que les autres réponses fournies et ne présente pas d'inconvénient.

Il préserve la chaîne de prototypes d'Erreur et toutes ses propriétés sur Erreur sans avoir à les connaître spécifiquement. Il a été testé sous Chrome, Firefox, Node et IE11.

La seule limitation est une entrée supplémentaire en haut de la pile d'appels. Mais cela est facilement ignoré.

Voici un exemple avec deux paramètres personnalisés:

function CustomError(message, param1, param2) {
    var err = new Error(message);
    Object.setPrototypeOf(err, CustomError.prototype);

    err.param1 = param1;
    err.param2 = param2;

    return err;
}

CustomError.prototype = Object.create(
    Error.prototype,
    {name: {value: 'CustomError', enumerable: false}}
);

Exemple d'utilisation:

try {
    throw new CustomError('Something Unexpected Happened!', 1234, 'neat');
} catch (ex) {
    console.log(ex.name); //CustomError
    console.log(ex.message); //Something Unexpected Happened!
    console.log(ex.param1); //1234
    console.log(ex.param2); //neat
    console.log(ex.stack); //stacktrace
    console.log(ex instanceof Error); //true
    console.log(ex instanceof CustomError); //true
}

Pour les environnements nécessitant un polyfil de setPrototypeOf:

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
};
9
Ben

Dans l'exemple ci-dessus, Error.apply (également Error.call) ne fait rien pour moi (Firefox 3.6/Chrome 5). Une solution de contournement que j'utilise est la suivante:

function MyError(message, fileName, lineNumber) {
    var err = new Error();

    if (err.stack) {
        // remove one stack level:
        if (typeof(Components) != 'undefined') {
            // Mozilla:
            this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
        }
        else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
            // Google Chrome/Node.js:
            this.stack = err.stack.replace(/\n[^\n]*/,'');
        }
        else {
            this.stack = err.stack;
        }
    }
    this.message    = message    === undefined ? err.message    : message;
    this.fileName   = fileName   === undefined ? err.fileName   : fileName;
    this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}

MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';
8
panzi

Comme certains l'ont dit, c'est assez facile avec ES6:

class CustomError extends Error { }

J'ai donc essayé cela dans mon application (Angular, TypeScript) et cela n'a tout simplement pas fonctionné. Après un certain temps, j'ai constaté que le problème venait de TypeScript: O

Voir https://github.com/Microsoft/TypeScript/issues/13965

C'est très dérangeant parce que si vous le faites:

class CustomError extends Error {}
​

try {
  throw new CustomError()
} catch(e) {
  if (e instanceof CustomError) {
    console.log('Custom error');
  } else {
    console.log('Basic error');
  }
}

En noeud ou directement dans votre navigateur, il affichera: Custom error

Essayez d’exécuter cela avec TypeScript dans votre projet sur un terrain de jeu TypeScript, il affichera Basic error...

La solution est la suivante:

class CustomError extends Error {
  // we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
  // otherwise we cannot use instanceof later to catch a given type
  public __proto__: Error;

  constructor(message?: string) {
    const trueProto = new.target.prototype;
    super(message);

    this.__proto__ = trueProto;
  }
}
5
maxime1992

Mes 2 centimes:

Pourquoi une autre réponse?

a) Parce que l’accès à la propriété Error.stack (comme dans certaines réponses) a un impact important sur les performances. 

b) Parce que ce n'est qu'une ligne.

c) Parce que la solution sur https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error ne semble pas conserver les informations sur la pile.

//MyError class constructor
function MyError(msg){
    this.__proto__.__proto__ = Error.apply(null, arguments);
};

exemple d'utilisation

http://jsfiddle.net/luciotato/xXyeB/

Qu'est ce que ça fait?

this.__proto__.__proto__ est MyError.prototype.__proto__, donc il définit le __proto__ FOR ALL INSTANCES de MyError sur une erreur spécifique nouvellement créée. Il conserve les propriétés et les méthodes de la classe MyError et place également les nouvelles propriétés Error (y compris .stack) dans la chaîne __proto__.

Problème évident:

Vous ne pouvez pas avoir plus d'une instance de MyError avec des informations utiles sur la pile.

N'utilisez pas cette solution si vous ne comprenez pas bien ce que fait this.__proto__.__proto__=.

3
Lucio M. Tato

Je veux juste ajouter à ce que d'autres ont déjà déclaré:

Pour vous assurer que la classe d'erreur personnalisée apparaît correctement dans la trace de pile, vous devez définir la propriété name du prototype de la classe d'erreur personnalisée sur la propriété name de la classe d'erreur personnalisée . C'est ce que je veux dire:

CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';

Ainsi, l'exemple complet serait:

    var CustomError = function(message) {
        var err = new Error(message);
        err.name = 'CustomError';
        this.name = err.name;
        this.message = err.message;
        //check if there is a stack property supported in browser
        if (err.stack) {
            this.stack = err.stack;
        }
        //we should define how our toString function works as this will be used internally
        //by the browser's stack trace generation function
        this.toString = function() {
           return this.name + ': ' + this.message;
        };
    };
    CustomError.prototype = new Error();
    CustomError.prototype.name = 'CustomError';

Quand tout est dit et fait, vous lancez votre nouvelle exception et elle ressemble à ceci (je l'ai essayé paresseusement dans les outils de développement de chrome):

CustomError: Stuff Happened. GASP!
    at Error.CustomError (<anonymous>:3:19)
    at <anonymous>:2:7
    at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
    at Object.InjectedScript.evaluate (<anonymous>:481:21)
3
Gautham C.

Dans Node comme d'autres l'ont déjà dit, c'est simple:

class DumbError extends Error {
    constructor(foo = 'bar', ...params) {
        super(...params);

        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, DumbError);
        }

        this.name = 'DumbError';

        this.foo = foo;
        this.date = new Date();
    }
}

try {
    let x = 3;
    if (x < 10) {
        throw new DumbError();
    }
} catch (error) {
    console.log(error);
}
2
Muhammad Umer

Comme les exceptions JavaScript sont difficiles à sous-classer, je ne les sous-classent pas. Je viens de créer une nouvelle classe d'exception et d'utiliser une erreur à l'intérieur de celle-ci. Je modifie la propriété Error.name afin qu'elle ressemble à mon exception personnalisée sur la console:

var InvalidInputError = function(message) {
    var error = new Error(message);
    error.name = 'InvalidInputError';
    return error;
};

La nouvelle exception ci-dessus peut être levée comme une erreur normale et fonctionnera comme prévu, par exemple:

throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string 

Avertissement: la trace de pile n'est pas parfaite, car elle vous mènera à l'endroit où la nouvelle erreur est créée et non à l'endroit où vous le lancez. Ce n'est pas un gros problème sur Chrome, car il vous fournit une trace de pile complète directement dans la console. Mais c'est plus problématique sous Firefox, par exemple.

2
Jonathan Benn

Comme indiqué dans la réponse de Mohsen, dans ES6, il est possible d'étendre les erreurs à l'aide de classes. C'est beaucoup plus facile et leur comportement est plus cohérent avec les erreurs natives ... mais malheureusement, il n'est pas simple de l'utiliser dans le navigateur si vous devez prendre en charge les navigateurs antérieurs à l'ES6. Voir ci-dessous quelques notes sur la façon dont cela pourrait être mis en œuvre, mais dans l'intervalle, je suggère une approche relativement simple qui intègre certaines des meilleures suggestions d'autres réponses:

function CustomError(message) {
    //This is for future compatibility with the ES6 version, which
    //would display a similar message if invoked without the
    //`new` operator.
    if (!(this instanceof CustomError)) {
        throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");
    }
    this.message = message;

    //Stack trace in V8
    if (Error.captureStackTrace) {
       Error.captureStackTrace(this, CustomError);
    }
    else this.stack = (new Error).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.name = 'CustomError';

Dans ES6, c'est aussi simple que:

class CustomError extends Error {}

... et vous pouvez détecter le support des classes ES6 avec try {eval('class X{}'), mais vous obtiendrez une erreur de syntaxe si vous tentez d'inclure la version ES6 dans un script chargé par des navigateurs plus anciens. Ainsi, le seul moyen de prendre en charge tous les navigateurs serait de charger un script distinct de manière dynamique (par exemple via AJAX ou eval()) pour les navigateurs prenant en charge ES6. Une complication supplémentaire est que eval() n'est pas pris en charge dans tous les environnements (en raison des stratégies de sécurité du contenu), ce qui peut ou peut ne pas être une considération pour votre projet.

Donc pour l'instant, soit la première approche ci-dessus ou simplement l'utilisation de Error directement sans essayer de l'étendre semble être ce qu'il y a de mieux pour le code devant supporter les navigateurs non-ES6.

Certaines personnes pourraient envisager une autre approche, qui consiste à utiliser Object.setPrototypeOf() lorsqu'il est disponible pour créer un objet d'erreur qui est une instance de votre type d'erreur personnalisée mais qui ressemble davantage à une erreur native dans la console (grâce à Réponse de Ben pour la recommandation). Voici mon point de vue sur cette approche: https://Gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Mais étant donné qu’un jour nous pourrons utiliser uniquement ES6, personnellement, je ne suis pas sûr que la complexité de cette approche en vaille la peine.

2
Matt Browne

Pour ce faire, vous devez renvoyer le résultat de apply du constructeur et définir le prototype de la manière javascript compliquée habituelle:

function MyError() {
    var tmp = Error.apply(this, arguments);
    tmp.name = this.name = 'MyError'

    this.stack = tmp.stack
    this.message = tmp.message

    return this
}
    var IntermediateInheritor = function() {}
        IntermediateInheritor.prototype = Error.prototype;
    MyError.prototype = new IntermediateInheritor()

var myError = new MyError("message");
console.log("The message is: '"+myError.message+"'") // The message is: 'message'
console.log(myError instanceof Error)                // true
console.log(myError instanceof MyError)              // true
console.log(myError.toString())                      // MyError: message
console.log(myError.stack)                           // MyError: message \n 
                                                     // <stack trace ...>

Les seuls problèmes avec cette façon de le faire à ce stade (je l'ai un peu réitéré) sont les suivants: 

  • les propriétés autres que stack et message ne sont pas incluses dans MyError et 
  • le stacktrace a une ligne supplémentaire qui n'est pas vraiment nécessaire. 

Le premier problème pourrait être résolu en parcourant toutes les propriétés d'erreur non énumérables en utilisant le truc utilisé dans cette réponse: Est-il possible d'obtenir les noms de propriété hérités non énumérables d'un objet? , mais ceci n'est pas supporté par ie <9. Le deuxième problème pourrait être résolu en déchirant cette ligne dans la trace de la pile, mais je ne sais pas comment le faire en toute sécurité (peut-être simplement en supprimant la deuxième ligne de e.stack.toString () ??).

1
B T

Décorateur d'erreur personnalisé

Ceci est basé sur la réponse de George Bailey , mais étend et simplifie l'idée originale. Il est écrit en CoffeeScript, mais il est facile à convertir en JavaScript. L'idée est d'étendre l'erreur personnalisée de Bailey avec un décorateur qui l'enveloppe, vous permettant de créer facilement de nouvelles erreurs personnalisées.

Note: Cela ne fonctionnera que dans V8. Error.captureStackTrace n'est pas pris en charge dans d'autres environnements.

Définir

Le décorateur prend un nom pour le type d'erreur et renvoie une fonction qui prend un message d'erreur et entoure le nom de l'erreur.

CoreError = (@message) ->

    @constructor.prototype.__proto__ = Error.prototype
    Error.captureStackTrace @, @constructor
    @name = @constructor.name

BaseError = (type) ->

    (message) -> new CoreError "#{ type }Error: #{ message }"

Utilisation

Maintenant, il est simple de créer de nouveaux types d'erreur.

StorageError   = BaseError "Storage"
SignatureError = BaseError "Signature"

Pour le plaisir, vous pouvez maintenant définir une fonction qui lance une SignatureError si elle est appelée avec trop d'arguments.

f = -> throw SignatureError "too many args" if arguments.length

Cela a été assez bien testé et semble fonctionner parfaitement sur V8, en maintenant le traçage, la position etc.

Remarque: L'utilisation de new est facultative lors de la création d'une erreur personnalisée.

0
Carl Smith

L'extrait montre tout.

function add(x, y) {
      if (x && y) {
        return x + y;
      } else {
        /**
         * 
         * the error thrown will be instanceof Error class and InvalidArgsError also
         */
        throw new InvalidArgsError();
        // throw new Invalid_Args_Error(); 
      }
    }

    // Declare custom error using using Class
    class Invalid_Args_Error extends Error {
      constructor() {
        super("Invalid arguments");
        Error.captureStackTrace(this);
      }
    }

    // Declare custom error using Function
    function InvalidArgsError(message) {
      this.message = `Invalid arguments`;
      Error.captureStackTrace(this);
    }
    // does the same magic as extends keyword
    Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);

    try{
      add(2)
    }catch(e){
      // true
      if(e instanceof Error){
        console.log(e)
      }
      // true
      if(e instanceof InvalidArgsError){
        console.log(e)
      }
    }
0
Amarpreet Singh

Je prendrais un pas en arrière et examinerais pourquoi tu veux faire ça? Je pense que le but est de traiter différentes erreurs différemment.

Par exemple, en Python, vous pouvez limiter l’instruction catch à catch MyValidationError, et vous souhaitez peut-être faire quelque chose de similaire en javascript.

catch (MyValidationError e) {
    ....
}

Vous ne pouvez pas faire cela en javascript. Il n'y aura qu'un seul bloc captif. Vous êtes censé utiliser une instruction if sur l'erreur pour déterminer son type.

catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }

Je pense que je jetterais plutôt un objet brut avec un type, un message et toute autre propriété que vous jugerez utile.

throw { type: "validation", message: "Invalid timestamp" }

Et quand vous attrapez l'erreur:

catch(e) {
    if(e.type === "validation") {
         // handle error
    }
    // re-throw, or whatever else
}
0
hasen