webentwicklung-frage-antwort-db.com.de

Fehler in Javascript mit ES6-Syntax und Babel erweitern

Ich versuche, Error mit ES6 und Babel zu erweitern. Es klappt nicht.

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

Das Error-Objekt erhält nie die richtige Nachricht.

Versuchen Sie es in Babel REPL .

Jetzt habe ich einige Lösungen auf SO ( zum Beispiel hier ) gesehen, aber alle scheinen sehr un-ES6-y zu sein. Wie mache ich das auf eine schöne ES6-Art? (Das funktioniert in Babel)

120
Karel Bílek

Basierend auf der Antwort von Karel Bílek würde ich die constructor etwas ändern:

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

Dadurch wird MyError im Stapel und nicht die generische Error ausgegeben.

Die Fehlermeldung wird außerdem der Stack-Ablaufverfolgung hinzugefügt, die in Karels Beispiel nicht vorhanden war.

Es wird auch captureStackTrace verwendet, falls verfügbar.

Mit Babel 6 benötigen Sie transform-builtin-extend ( npm ), damit dies funktioniert.

166
Lee Benson

diese Antwort kombinieren , diese Antwort und diesen Code Ich habe diese kleine "Helfer" -Klasse gemacht, die scheint gut zu funktionieren.

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

Probiere REPL aus

38
Karel Bílek

Um dies endlich zur Ruhe zu bringen. In Babel 6 ist es ausdrücklich so, dass die Entwickler keine Unterstützung vom eingebauten aus erweitern. Obwohl dieser Trick keine Unterstützung bei Dingen wie Map hilft , Set usw. funktioniert für Error. Dies ist wichtig, da eine der Kernideen einer Sprache, die eine Ausnahme auslösen kann, darin besteht, benutzerdefinierte Fehler zuzulassen. Dies ist doppelt wichtig, da Versprechen nützlicher werden, da sie darauf ausgelegt sind, einen Fehler zurückzuweisen .

Die traurige Wahrheit ist, dass Sie dies in ES2015 noch auf die alte Weise durchführen müssen.

Beispiel in Babel REPL

Benutzerdefiniertes Fehlermuster

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

Auf der anderen Seite gibt es ein Plugin für Babel 6, um dies zu ermöglichen.

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

Update: (Stand 29.09.2016) Nach einigen Tests scheint es, dass babel.io nicht alle Asserts korrekt berücksichtigt (von a ausgehend) Benutzerdefinierter erweiterter Fehler). Aber in Ember.JS funktioniert der Erweiterungsfehler wie erwartet: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce

25
Sukima

Edit: Änderungen in TypeScript 2.1 brechen

Das Erweitern von integrierten Inhalten wie Error, Array und Map funktioniert möglicherweise nicht mehr.

Als Empfehlung können Sie den Prototyp unmittelbar nach einem Superaufruf (...) manuell anpassen.

Das Bearbeiten von Lee Bensons Originalantwort funktioniert ein bisschen für mich. Dies fügt der Instanz auch stack und zusätzliche Methoden der ExtendableError-Klasse hinzu. 

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

Mit den neuesten Änderungen in Babel 6 finde ich transform-builtin-extend funktioniert nicht mehr. Ich habe diesen gemischten Ansatz verwendet:

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;

und

import MyError from './MyError';

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

Als Ergebnis bestehen alle diese Tests:

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');
8
Diego Ferri

Zitate

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

Dank this.stack = (new Error()).stack;-Aufruf ist kein super()-Trick erforderlich.

Die obigen Codes können den Stack-Trace jedoch nur ausgeben, wenn this.stack = (new Error()).stack; oder Error.captureStackTrace(this, this.constructor.name); in Babel aufgerufen wird. IMO, es ist vielleicht ein Thema hier.

Tatsächlich kann der Stack-Trace mit diesen Code-Snippets unter Chrome console und Node.js v4.2.1 ausgegeben werden.

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

Ausgabe von 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)

Ausgabe von 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
zangw

Zusätzlich zur @Zangw-Antwort können Sie Ihre Fehler folgendermaßen definieren:

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

was den korrekten Namen, die Nachricht und den Stacktrace auslöst:

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

Ich versuche, Error mit ES6 zu erweitern

Diese class MyError extends Error {…}-Syntax ist korrekt.

Beachten Sie, dass Transpiler immer noch Probleme haben, von eingebauten Objekten zu erben. In Ihrem Fall,

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

scheint das Problem zu beheben.

2
Bergi

Da die akzeptierte Antwort nicht mehr funktioniert, können Sie alternativ immer eine Fabrik verwenden ( 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
Melbourne2991

Das funktioniert bei mir:

/**
 * @class AuthorizationError
 * @extends {Error}
 */
export class AuthorizationError extends Error {
    message = 'UNAUTHORIZED';
    name = 'AuthorizationError';
}
1
Michael Liquori

Wie @sukima erwähnt, können Sie native JS nicht erweitern. Die Frage des OP kann nicht beantwortet werden. 

Ähnlich wie in Melbourne2991s Antwort habe ich eher eine Fabrik verwendet, folgte aber der Empfehlung von MDN für Kundenfehlertypen .

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
Eric H.

Ich bevorzuge eine stärkere Syntax als oben beschrieben. Zusätzliche Methoden bei error type helfen Ihnen dabei, console.log oder etwas anderes zu erstellen.

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

Um diesen Code zu testen, können Sie etwas Ähnliches ausführen:

try {
    throw new CustomError('Custom error was thrown!');
} catch (e) {
    const message = e.getPrettyMessage();

    console.warn(message);
}

Die Erweiterung von CustomError ist erwünscht. Es ist möglich, dem erweiterten Typ bestimmte Funktionen hinzuzufügen oder vorhandene zu überschreiben. Zum Beispiel.

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}.`;
    }
}
1
B. Bohdan

Ich benutze Babel nicht, aber in ES6 scheint das Folgende gut zu funktionieren:

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

Testen von 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 ...'

Wie Sie sehen, enthält der Stack sowohl den Fehlernamen als auch die Fehlermeldung. Ich bin nicht sicher, ob mir etwas fehlt, aber alle anderen Antworten scheinen die Dinge zu komplizieren.

0
JHH