ES6 sözdizimi ve Babel ile Javascript'te Genişletme Hatası


132

ES6 ve Babel ile Hatayı genişletmeye çalışıyorum. Çalışmıyor.

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

Error nesnesi asla doğru mesaj setini almaz.

Babel REPL'de deneyin .

Şimdi SO üzerine birkaç çözüm gördüm ( örneğin burada ), ama hepsi ES6-y gibi görünüyor. Güzel, ES6 yöntemiyle nasıl yapılır? (Bu Babel'de çalışıyor)


2
Babel REPL bağlantınızı takip etmeniz artık doğru çalıştığını gösteriyor. O zamandan beri Babel'de düzeltilen bir hata olduğunu düşünüyorum.
kybernetikos

Yanıtlar:


188

Karel Bílek'in cevabına dayanarak, şunlarda küçük bir değişiklik yapardım 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);

Bu MyError, jenerik değil yığında yazdıracaktır Error.

Ayrıca, Karel'in örneğinde eksik olan hata mesajını yığın izlemesine de ekleyecektir.

captureStackTraceVarsa da kullanacaktır .

Babel 6 ile bunun çalışması için transform-builtin- ext ( npm ) gerekir.


1
@MichaelYounkin if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor.name) } else { this.stack = (new Error(message)).stack; } . Daha 'yerel' bir çağrı yığını sağladığından ve hata nesnesinin adını yazdırdığından, mevcutsa bu işlevi kullanmanın daha iyi olduğunu iddia ediyorum. Elbette, bunu yalnızca sunucu tarafında da kullanıyorsanız (Düğüm), o zaman bu da bir sorun değildir.
Lee Benson

4
@MichaelYounkin Bunun bir olumsuz oyu hak ettiğini düşünmüyorum. OP, ES6'daki hataların genişletilmesinden bahsetti. Bu mantığa göre, ES6'nın neredeyse tamamı en az bir tarayıcıda eksiktir. Benim çözümüm (eklenen işlev kontrolü ile), en yaygın kullanılan tarayıcıda yerel kapsama, diğer her birinde geri dönüş ve Node.js'de% 100 kapsama sağlar. Kabul ediyorum, eğer sınıf adında sürekli olarak hata this.stack = (new Error(message)).stackyaparsanız, o zaman bunu alırsınız ... ama pratikte, bu muhtemelen çok büyük bir anlaşma değildir.
Lee Benson

6
Bu Babel 6'da çalışmıyor:new MyError('foo') instanceof MyError === false
Sukima

5
Bu kod, babel ile NPM modülü olarak önceden derlenmiştir: extendable-error-class npmjs.com/package/extendable-error-class , babel-plugin-transform-builtin-
Extend'e

3
this.message = message;ile gereksizsuper(message);
mathieug

39

Bu cevabı , bu cevabı ve bu kodu birleştirerek , iyi çalışıyor gibi görünen bu küçük "yardımcı" sınıfı yaptım.

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

REPL olarak deneyin


1
this.stack = (new Error(message)).stack;- aksi takdirde yığın izlemede mesaj eksiktir
Lee Benson

3
Bunun gerektiği gibi çalışmadığından şüpheleniyorum çünkü bunu yaparsanız: console.log (Myerror ExtendableError örneği); hala yanlış diyor ..
Mauno Vähä

4
Aynı sorun, CustomError örneğinin kullanılması işe yaramıyor, instanceof'u kullanamıyorsanız genişletme noktası nedir?
gre

messageHata yığın yapıcısına eklenerek iyileştirilebilir , böylece atıldığında yığının en üstünde doğru mesajı gösterir:this.stack = (new Error(message)).stack;
Sebastien

1
myerror.nameşimdi "Hata" döndürür. Emin değil bu sukima cevabı aşağıda @ babel.See sonraki sürümlerinde için ilişkili olup olmadığını
Eric H.

27

Sonunda bunu dinlendirmek için. Babel 6 yılında geliştiricilerin gerektiğini vurgulamaktadır desteklemeyen inşa uzanan. Bu hüner olsa olmaz şeylerle yardım ister Map, Setonun için çalışır vb Error. Bir istisna atabilen bir dilin temel fikirlerinden biri özel Hatalara izin vermek olduğu için bu önemlidir. Sözler bir Hatayı reddetmek üzere tasarlandıklarından daha kullanışlı hale geldikçe bu iki kat önemlidir .

Üzücü gerçek şu ki, bunu ES2015'te eski şekilde yapmanız gerekiyor.

Babel REPL'deki örnek

Özel Hata modeli

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

Öte yandan Babel 6'nın buna izin vermesi için bir eklenti var.

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

Güncelleme: (2016-09-29 itibariyle) Bazı testlerden sonra babel.io'nun tüm iddiaları doğru bir şekilde hesaba katmadığı anlaşılıyor (özel bir genişletilmiş Hatadan itibaren). Ancak Ember.JS'de genişleyen Hata beklendiği gibi çalışıyor: https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce


evet, bağlantılı babel eklentisi ile kabul edilen cevapla doğru şekilde çalışıyor. (Ancak, ortaya çıkan dosya Düğümde çalışmıyor, çünkü görünüşe göre Yansıtma özelliği yok)
Karel Bílek

Bir merak olarak, ES2016 teknik özellikleri yerleşiklerin genişletilebilir olduğunu söylüyorsa neden v8 ve Babel'in es5 gibi sanal makineleri buna karşı çıkıyor? Bir sınıfın, bir prototip zincirinin diğer prototiplerden gelebileceği gibi bir sınıfı genişletebileceği makul bir beklenti değil mi? Neden böyle bir seramoni ihtiyacı bir eklentide saklı?
Sukima

Çoğu kullanım durumu sadece davranışı paylaşan basit nesneler yapmak istediğinde bu özellikle sinir bozucudur. Kullanılabilecek özel bir hata Error.toString(). Bunu başarmak için özel çemberler ve dönüşler yapma ihtiyacı, çoğu geliştiricinin bundan kaçınacağı ve Hatalar yerine dizeler atmak gibi kötü uygulamalara başvuracağı anlamına gelir. Veya nesneler gibi kendi Haritalarını yapmak. Neden bu tür OOP yöntemlerini caydırmak gerekiyor?
Sukima

Bence buna karşı değiller, bu sadece teknik bir mesele. Çok da emin değilim! Onlara sorabilirsiniz :) projeler oldukça açık
Karel Bílek

Şimdiye kadar konuyla ilgili simüler sorulardan gelen tüm yanıtlar "babel bunu desteklemiyor" şeklinde bırakıldı, bunun konuşmanın sonu olduğunu düşündüm. Benim sığır etim, destek eksikliğinin yaygın bir OOP deyimini zorlaştırması ve hatta ortak wokerlerle onları kazan plakası zaafını aşmak için savaşmak zorunda kaldım. Keşke burada temiz bir alternatif çözüm olsaydı. Görünüşe göre bir eklenti eklemek en iyi seçenek.
Sukima

15

Düzenleme : Typcript 2.1'deki değişiklikleri bozma

Hata, Dizi ve Harita gibi yerleşik öğeleri genişletmek artık çalışmayabilir.

Bir öneri olarak, herhangi bir süper (...) aramadan hemen sonra prototipi manuel olarak ayarlayabilirsiniz.

Lee Benson orijinal cevabını düzenlemek benim için biraz işe yarıyor. Bu ayrıca örneğe sınıfın stackek yöntemlerini de ekler ExtendableError.

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

1
Sen buna gerek Object.setPrototypeOfde MyErrorayrıca yapıcısı. stackoverflow.com/a/41102306/186334 github.com/Microsoft/TypeScript-wiki/blob/master/…
CallMeLaNN

10

Babel 6'daki en son değişikliklerle, transform-builtin-Extend'in artık çalışmadığını görüyorum . Bu karma yaklaşımı kullandım:

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;

ve

import MyError from './MyError';

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

Sonuç olarak tüm bu testler geçer:

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

6

Alıntı yapmak

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

Çağrı this.stack = (new Error()).stack;sayesinde numara yapmaya gerek yok super().

Yukarıdaki kodlar, Babel'de çağrılmadıkça this.stack = (new Error()).stack;veya Error.captureStackTrace(this, this.constructor.name);çağrılmadıkça yığın izleme çıktısını alamaz . IMO, burada bir sorun olabilir.

Aslında, yığın izleme bu kod parçacıkları altında Chrome consoleve Node.js v4.2.1bu kod parçacıklarıyla çıkarılabilir .

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

Çıktı 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)

Çıktı 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 cevabına ek olarak, hatalarınızı şu şekilde tanımlayabilirsiniz:

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

doğru adı, mesajı ve yığın izini atacak:

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
Bu işe yaramazsa: new MyError('foo') instanceof MyError === false.
Sukima

1
Oluyor Node.js v7.7.3.
Günar Gessner

2

ES6 ile Hatayı uzatmaya çalışıyorum

Bu class MyError extends Error {…}sözdizimi doğrudur.

Aktarıcıların yerleşik nesnelerden devralma konusunda hala sorun yaşadığına dikkat edin. Senin durumunda,

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

sorunu çözüyor gibi görünüyor.


Doğru! Ama yine de mesaj belirlenmedi - yeni bir örnek yazacağım.
Karel Bílek

Şimdi örneği yeniden yazdım
Karel Bílek


Görünüşe göre "süper (m)" boş bir nesne döndürecektir. Yani Object.assign yardımcı olmuyor.
Karel Bílek

@ KarelBílek: Hangi tarayıcıyı kullanıyorsunuz? Error.call()benim için yeni bir hata örneği döndürüyor.
Bergi

2

Buna göre, kabul edilen yanıt artık işe yaramazsa , alternatif olarak her zaman bir fabrika kullanabilirsiniz ( 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);


Gerekli babel eklentilerine sahipseniz, kabul edilen cevap hala benim için çalışıyor. Yine de bu cevap için teşekkürler!
Karel Bílek

2

Yukarıda anlatılandan daha güçlü sözdizimi tercih ederim. Hata türündeki ek yöntemler, güzel console.logveya başka bir şey yaratmanıza yardımcı olacaktır .

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

Bu kodu test etmek için benzer bir şey çalıştırabilirsiniz:

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

    console.warn(message);
}

Türün genişletilmesi CustomErroraçığız. Genişletilmiş türe bazı özel işlevler eklemek veya var olanı geçersiz kılmak mümkündür. Örneğin.

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

@Sukima'nın da belirttiği gibi, yerel JS'yi genişletemezsiniz. OP'nin sorusu cevaplanamaz.

Melbourne2991'in cevabına benzer şekilde , bunun yerine bir fabrika kullandım, ancak MDN'nin müşteri hatası türleri için önerisini takip ettim .

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

Bu benim için çalışıyor:

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

0

Babel kullanmıyorum, ancak ES6'da, aşağıdakiler benim için iyi çalışıyor gibi görünüyor:

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

REPL'den test etme:

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

Gördüğünüz gibi, yığın hem hata adını hem de mesajı içerir. Bir şeyi kaçırdığımdan emin değilim, ancak diğer tüm cevaplar işleri aşırı karmaşık hale getiriyor.


0

@Lee Benson çözümünü bu şekilde biraz geliştirdim:

extendableError.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

bir hata örneği

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 

Ardından, uygulamaya özgü bazı durumlarda neyin farklı şekilde yapılacağına karar vermek için seçenek belirleyicilerine sahipken hataları gruplayabilirsiniz.

new AuthorizationError ("The user must be a seller to be able to do a discount", AuthorizationError.errors.BAD_PROFILE_TYPE )
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.