Próbuję rozszerzyć Error o ES6 i Babel. To nie działa.
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
Obiekt Error nigdy nie otrzymuje odpowiedniego zestawu komunikatów.
Teraz widziałem kilka rozwiązań na SO ( na przykład tutaj ), ale wszystkie wydają się bardzo nie-ES6-y. Jak to zrobić fajnie, ES6? (To działa w Babel)
javascript
ecmascript-6
babeljs
transpiler
Karel Bílek
źródło
źródło
Odpowiedzi:
Opierając się na odpowiedzi Karela Bíleka, wprowadziłbym małą zmianę w
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);
Spowoduje to wydrukowanie
MyError
na stosie, a nie na ogólnymError
.Doda również komunikat o błędzie do śladu stosu - czego brakowało w przykładzie Karela.
Będzie również używać,
captureStackTrace
jeśli jest dostępny.W przypadku Babel 6, aby to zadziałało , potrzebujesz wbudowanego przekształcania ( npm ).
źródło
if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor.name) } else { this.stack = (new Error(message)).stack; }
. Twierdziłbym, że lepiej jest użyć tej funkcji, jeśli jest dostępna, ponieważ zapewnia ona bardziej „natywny” stos wywołań i wyświetla nazwę obiektu błędu. Oczywiście, jeśli używasz tego również wyłącznie po stronie serwera (Node), to również nie stanowi problemu.this.stack = (new Error(message)).stack
dostaniesz to ... ale w praktyce prawdopodobnie nie jest to wielka sprawa.new MyError('foo') instanceof MyError === false
extendable-error-class
npmjs.com/package/extendable-error-class, co jest wygodne, aby uniknąć zależności od babel-plugin-transform-this.message = message;
jest zbędny zsuper(message);
Łącząc tę odpowiedź , tę odpowiedź i ten kod , stworzyłem tę małą klasę „pomocniczą”, która wydaje się działać dobrze.
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);
Spróbuj w REPL
źródło
this.stack = (new Error(message)).stack;
- w przeciwnym razie wiadomość zniknie ze stosumessage
w konstruktorze stosu błędów, aby po wyrzuceniu wyświetlał właściwy komunikat na górze stosu:this.stack = (new Error(message)).stack;
myerror.name
teraz zwraca „Błąd”. Nie wiem, czy jest to związane z nowszych wersjach babel.See @ sukima na odpowiedź poniżejAby w końcu to odłożyć. W Babel 6 jest jednoznaczne, że twórcy nie obsługują rozciągający się od wybudowania. Chociaż ta sztuczka nie pomaga z rzeczy, jak
Map
,Set
itp to zadziałaError
. Jest to ważne, ponieważ jedną z podstawowych idei języka, który może zgłosić wyjątek, jest zezwolenie na niestandardowe błędy. Jest to podwójnie ważne, ponieważ obietnice stają się bardziej użyteczne, ponieważ mają na celu odrzucenie błędu .Smutną prawdą jest to, że nadal musisz to robić w stary sposób w ES2015.
Przykład w Babel REPL
Niestandardowy wzorzec błędu
class MyError { constructor(message) { this.name = 'MyError'; this.message = message; this.stack = new Error().stack; // Optional } } MyError.prototype = Object.create(Error.prototype);
Z drugiej strony jest wtyczka dla Babel 6, która to umożliwia.
https://www.npmjs.com/package/babel-plugin-transform-builtin-extend
Aktualizacja: (stan na 2016-09-29) Po kilku testach okazuje się, że babel.io nie uwzględnia poprawnie wszystkich potwierdzeń (rozszerzając niestandardowy rozszerzony błąd). Ale w Ember.JS błąd rozszerzania działa zgodnie z oczekiwaniami: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce
źródło
Error.toString()
. Konieczność wykonywania specjalnych kółek i zakrętów, aby to osiągnąć, oznacza, że większość programistów będzie tego unikać i ucieka się do złych praktyk, takich jak rzucanie strunami zamiast błędów. Albo tworzyć własne mapy jak obiekty. Skąd potrzeba odstraszania takich metod OOP?Edycja : istotne zmiany w skrypcie maszynowym 2.1
Edycja oryginalnej odpowiedzi Lee Bensona trochę mi odpowiada. To również dodaje
stack
i dodatkowe metodyExtendableError
klasy do instancji.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);
źródło
Object.setPrototypeOf
wMyError
konstruktorze również. stackoverflow.com/a/41102306/186334 github.com/Microsoft/TypeScript-wiki/blob/master/ ...Wraz z ostatnimi zmianami w babel 6 uważam, że wbudowane rozszerzenie transformacji już nie działa. Skończyło się na tym mieszanym podejściu:
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;
i
import MyError from './MyError'; export default class MyChildError extends MyError { constructor (message) { super(message); } }
W rezultacie wszystkie te testy przechodzą:
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');
źródło
Cytowanie
class MyError extends Error { constructor(message) { super(message); this.message = message; this.name = 'MyError'; } }
Chociaż powyższe kody nie mogą wyprowadzać śladu stosu, chyba że
this.stack = (new Error()).stack;
lubError.captureStackTrace(this, this.constructor.name);
są wywoływane w Babel . IMO, to może jeden problem tutaj.W rzeczywistości ślad stosu można wyprowadzić w ramach tych fragmentów kodu
Chrome console
i zaNode.js v4.2.1
pomocą tych fragmentów kodu.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);
Wyjście
Chrome console
.Wyjście
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
źródło
Oprócz odpowiedzi @zangw możesz zdefiniować swoje błędy w ten sposób:
'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');
który wyrzuci poprawną nazwę, wiadomość i stos:
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
źródło
new MyError('foo') instanceof MyError === false
.Node.js v7.7.3
.Ta
class MyError extends Error {…}
składnia jest poprawna.Zauważ, że transpilery nadal mają problemy z dziedziczeniem po obiektach wbudowanych. W Twoim przypadku,
var err = super(m); Object.assign(this, err);
wydaje się, że rozwiązuje problem.
źródło
Error.call()
zwraca dla mnie nową instancję błędu.Biorąc pod uwagę to, zaakceptowana odpowiedź już nie działa, zawsze możesz użyć fabryki jako alternatywy ( repl ):
źródło
Wolę mocniejszą składnię niż opisana powyżej. Dodatkowe metody typu error pomogą ci stworzyć ładną
console.log
lub coś innego.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; } }
Aby przetestować ten kod, możesz uruchomić coś podobnego:
try { throw new CustomError('Custom error was thrown!'); } catch (e) { const message = e.getPrettyMessage(); console.warn(message); }
CustomError
Mile widziane jest rozszerzenie typu. Możliwe jest dodanie określonej funkcjonalności do typu rozszerzonego lub zastąpienie istniejącego. Na przykład.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}.`; } }
źródło
Jak wspomina @sukima, nie można rozszerzać natywnego JS. Nie można odpowiedzieć na pytanie PO.
Podobnie jak odpowiedź Melbourne2991 , korzystałem raczej z fabryki, ale postępowałem zgodnie z zaleceniami MDN dotyczącymi typów błędów klientów .
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; }
źródło
To działa dla mnie:
/** * @class AuthorizationError * @extends {Error} */ export class AuthorizationError extends Error { message = 'UNAUTHORIZED'; name = 'AuthorizationError'; }
źródło
Nie używam Babel, ale w zwykłym ES6 wydaje mi się, że działa dobrze:
class CustomError extends Error { constructor(...args) { super(...args); this.name = this.constructor.name; } }
Testowanie z 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 ...'
Jak widać, stos zawiera zarówno nazwę błędu, jak i komunikat. Nie jestem pewien, czy czegoś mi brakuje, ale wszystkie inne odpowiedzi wydają się zbytnio komplikować.
źródło
Poprawiłem trochę rozwiązanie @Lee Benson w ten sposób:
extableError.js
class ExtendableError extends Error { constructor(message, errorCode) { super(message); this.name = this.constructor.name; this.errorCode = errorCode if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor); } else { this.stack = (new Error(message)).stack; } } } export default ExtendableError
przykład błędu
import ExtendableError from './ExtendableError' const AuthorizationErrors = { NOT_AUTHORIZED: 401, BAD_PROFILE_TYPE: 402, ROLE_NOT_ATTRIBUTED: 403 } class AuthorizationError extends ExtendableError { static errors = AuthorizationErrors } export default AuthorizationError
Następnie możesz grupować błędy, mając specyfikatory opcji, aby zdecydować, co zrobić inaczej w niektórych sytuacjach specyficznych dla aplikacji
new AuthorizationError ("The user must be a seller to be able to do a discount", AuthorizationError.errors.BAD_PROFILE_TYPE )
źródło