J'essaie d'étendre Error avec ES6 et Babel. Ça ne marche pas.
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 Error ne reçoit jamais le bon ensemble de messages.
Maintenant, j'ai vu quelques solutions sur SO ( par exemple ici ), mais elles semblent toutes très différentes de l'ES6-y. Comment le faire à la manière de Nice, ES6? (Cela fonctionne à Babel)
Sur la base de la réponse de Karel Bílek, je voudrais apporter 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);
Ceci imprimera MyError
dans la pile et non le générique Error
.
Il ajoutera également le message d'erreur à la trace de la pile - manquante dans l'exemple de Karel.
Il utilisera également captureStackTrace
s'il est disponible.
Avec Babel 6, vous avez besoin de transform-builtin-extend ( npm ) pour que cela fonctionne.
En combinant cette réponse , cette réponse et ce code , j'ai créé cette petite classe "d'aide" qui semble fonctionner correctement.
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);
Pour finalement mettre cela au repos. Dans Babel 6, il est explicite que les développeurs ne prend pas en charge s’étendant de intégré. Bien que cette astuce ne le fera pas aide avec des choses comme Map
, Set
, etc., cela fonctionne pour Error
. Ceci est important car l'une des idées de base d'un langage qui peut générer une exception est d'autoriser les erreurs personnalisées. Ceci est d'autant plus important que les promesses deviennent plus utiles puisqu'elles sont conçues pour rejeter une erreur .
La triste vérité, c’est que vous avez toujours besoin de procéder de la même manière dans ES2015.
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 permettant cela.
https://www.npmjs.com/package/babel-plugin-transform-builtin-extend
Mise à jour: (à partir du 2016-09-29) Après quelques tests, il apparaît que babel.io ne comptabilise pas correctement toutes les assertions (à partir d'une erreur étendue personnalisée). Mais dans Ember.JS, EXText Error fonctionne comme prévu: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce
Edit: Interrompre les modifications dans TypeScript 2.1
Les extensions intégrées telles que Error, Array et Map risquent de ne plus fonctionner.
A titre de recommandation, vous pouvez ajuster manuellement le prototype immédiatement après tout appel super (...).
Editer Lee Benson réponse originale un peu fonctionne 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 {
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);
Avec les dernières modifications apportées à babel 6, je constate que transform-builtin-extend 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 réussissent:
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';
}
}
Il n’est pas nécessaire de faire appel à
this.stack = (new Error()).stack;
grâce à l’appelsuper()
.
Bien que les codes ci-dessus ne puissent pas générer la trace de pile, sauf si this.stack = (new Error()).stack;
ou Error.captureStackTrace(this, this.constructor.name);
est appelé dans Babel . OMI, c'est peut-être un problème ici.
En réalité, la trace de la 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
En plus de @zangw answer, vous pouvez définir vos erreurs comme suit:
'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 jettera le nom, le message et le stacktrace correct
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
J'essaie d'étendre Error avec ES6
Cette syntaxe class MyError extends Error {…}
est correcte.
Notez que les transpilers ont toujours des problèmes d'héritage d'objets intégrés. Dans ton cas,
var err = super(m);
Object.assign(this, err);
semble résoudre le problème.
Etant donné que 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);
Cela fonctionne pour moi:
/**
* @class AuthorizationError
* @extends {Error}
*/
export class AuthorizationError extends Error {
message = 'UNAUTHORIZED';
name = 'AuthorizationError';
}
Comme @sukima le mentionne, vous ne pouvez pas étendre JS natif. On ne peut pas répondre à la question du PO.
Similaire à la réponse de Melbourne2991 , j'ai plutôt utilisé une usine, mais j'ai suivi la recommandation de MDN concernant 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;
}
Je préfère une syntaxe plus forte que celle décrite ci-dessus. Des méthodes supplémentaires au type d'erreur vous aideront à créer un joli console.log
ou quelque chose d'autre.
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;
}
}
Pour tester ce code, vous pouvez exécuter quelque chose de similaire:
try {
throw new CustomError('Custom error was thrown!');
} catch (e) {
const message = e.getPrettyMessage();
console.warn(message);
}
Les extensions de type CustomError
sont les bienvenues. Il est possible d'ajouter des fonctionnalités spécifiques au type étendu ou de remplacer les fonctions existantes. Par exemple.
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}.`;
}
}
Ne pas utiliser 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 constater, la pile contient à la fois le nom de l'erreur et le message. Je ne suis pas sûr d'avoir oublié quelque chose, mais toutes les autres réponses semblent trop compliquer les choses.