ecmascript - Error de extensión en Javascript con sintaxis ES6 y Babel
javascript es6 pdf (13)
Estoy tratando de extender Error con ES6
Esa
class MyError extends Error {…}
es correcta.
Tenga en cuenta que los transpiladores todavía tienen problemas para heredar de los objetos incorporados. En tu caso,
var err = super(m);
Object.assign(this, err);
Parece solucionar el problema.
Estoy tratando de extender Error con ES6 y Babel. No esta funcionando.
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
El objeto Error nunca obtiene el conjunto de mensajes correcto.
Ahora he visto algunas soluciones en SO ( por ejemplo aquí ), pero todas parecen muy poco ES6-y. ¿Cómo hacerlo de una manera agradable, ES6? (Eso está funcionando en Babel)
Además de la respuesta @zangw, puede definir sus errores de esta manera:
''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'');
que arrojará el nombre, mensaje y seguimiento de pila correctos:
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
Basado en la respuesta de Karel Bílek, haría un pequeño cambio en el
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);
Esto imprimirá
MyError
en la pila, y no el
Error
genérico.
También agregará el mensaje de error al seguimiento de la pila, que faltaba en el ejemplo de Karel.
También usará
captureStackTrace
si está disponible.
Con Babel 6, necesita transform-builtin-extend ( npm ) para que esto funcione.
Combinando esta respuesta , esta respuesta y este código , he creado esta pequeña clase "auxiliar", que parece funcionar bien.
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);
Como @sukima menciona, no puede extender JS nativo. La pregunta del OP no puede ser respondida.
Similar a la respuesta de Melbourne2991 , utilicé una fábrica, pero seguí la recomendación de MDN para los tipos de error del cliente .
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;
}
Con los últimos cambios en babel 6, encuentro que transform-builtin-extend ya no funciona. Terminé usando este enfoque mixto:
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;
y
import MyError from ''./MyError'';
export default class MyChildError extends MyError {
constructor (message) {
super(message);
}
}
Como resultado, todas estas pruebas pasan:
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'');
Dado esto, la respuesta aceptada ya no funciona, siempre se puede usar una fábrica como alternativa ( 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);
Esto funciona para mi:
/**
* @class AuthorizationError
* @extends {Error}
*/
export class AuthorizationError extends Error {
message = ''UNAUTHORIZED'';
name = ''AuthorizationError'';
}
Para finalmente poner esto a descansar.
En Babel 6 es explícito que los desarrolladores
no son compatibles con la
extensión desde el incorporado. Aunque este truco
no
ayudará con cosas como
Map
,
Set
, etc., funciona para
Error
.
Esto es importante ya que una de las ideas centrales de un lenguaje que puede generar una excepción es permitir errores personalizados.
Esto es doblemente importante ya que las Promesas se vuelven más útiles, ya que están diseñadas para
rechazar un Error
.
La triste verdad es que aún necesita realizar esto a la antigua usanza en ES2015.
Patrón de error personalizado
class MyError {
constructor(message) {
this.name = ''MyError'';
this.message = message;
this.stack = new Error().stack; // Optional
}
}
MyError.prototype = Object.create(Error.prototype);
Por otro lado, hay un complemento para Babel 6 para permitir esto.
Actualización: (a partir del 29/09/2016) Después de algunas pruebas, parece que babel.io no tiene en cuenta correctamente todas las afirmaciones (que se extienden desde un error extendido personalizado). Pero en Ember.JS extender Error funciona como se esperaba: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce
Prefiero una sintaxis más fuerte que la descrita anteriormente.
Los métodos adicionales en el tipo de error te ayudarán a crear un bonito
console.log
o algo más.
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;
}
}
Para probar este código, puede ejecutar algo similar:
try {
throw new CustomError(''Custom error was thrown!'');
} catch (e) {
const message = e.getPrettyMessage();
console.warn(message);
}
La extensión del tipo
CustomError
es bienvenida.
Es posible agregar alguna funcionalidad específica al tipo extendido o anular el existente.
Por ejemplo.
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}.`;
}
}
Sin usar Babel, pero en ES6 simple, lo siguiente parece funcionar bien para mí:
class CustomError extends Error {
constructor(...args) {
super(...args);
this.name = this.constructor.name;
}
}
Prueba 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 ...''
Como puede ver, la pila contiene tanto el nombre del error como el mensaje. No estoy seguro de si me falta algo, pero todas las otras respuestas parecen complicar demasiado las cosas.
class MyError extends Error {
constructor(message) {
super(message);
this.message = message;
this.name = ''MyError'';
}
}
No hay necesidad de
this.stack = (new Error()).stack;
truco gracias a la llamadasuper()
.
Aunque los códigos anteriores no pueden generar el seguimiento de la pila a menos que
this.stack = (new Error()).stack;
o
Error.captureStackTrace(this, this.constructor.name);
se invoca en
Babel
.
OMI, tal vez sea un problema aquí.
En realidad, el seguimiento de la pila se puede generar en la
Chrome console
y
Node.js v4.2.1
con estos fragmentos de código.
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);
Salida de la
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)
Salida 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
Editar : Rompiendo cambios en Typecript 2.1
Es posible que las extensiones integradas como Error, Array y Mapa ya no funcionen.
Como recomendación, puede ajustar manualmente el prototipo inmediatamente después de cualquier super (...) llamada.
Editar la respuesta original de Lee Benson funciona un poco para mí.
Esto también agrega
stack
y métodos adicionales de la clase
ExtendableError
a la instancia.
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);