ES6構文とBabelを使用したJavascriptのエラーの拡張


132

ES6とBabelでエラーを拡張しようとしています。うまくいっていません。

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オブジェクトが正しいメッセージセットを取得することはありません。

Babel REPLで試してください

今、私はSOでいくつかのソリューションを見ました(たとえば、ここ)が、それらはすべて非常に非ES6-yのようです。それをES6の素敵な方法で行うには?(それはバベルで働いています)


2
Babel REPLへのリンクをたどることは、現在正しく機能していることを示しているようです。私はそれが修正されたバベルのバグだったと思います。
kybernetikos 2016

回答:


188

カレルビレクの回答に基づいて、私は次のように少し変更します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);

これはMyError、ジェネリックではなくスタックに出力されますError

また、エラーメッセージをスタックトレースに追加します。これは、Karelの例にはありませんでした。

captureStackTrace利用可能な場合にも使用します。

Babel 6でこれを機能させるには、transform-b​​uiltin-extendnpm)が必要です。


1
@MichaelYounkin if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor.name) } else { this.stack = (new Error(message)).stack; } 。よりネイティブなコールスタックを提供し、エラーオブジェクトの名前を出力するため、この関数が使用可能な場合は、この関数を使用する方が良いと私は主張します。もちろん、これをサーバー側(Node)でのみ使用している場合も問題ありません。
リーベンソン

4
@MichaelYounkin私はこれに反対する価値があるとは思いません。OPはES6での拡張エラーについて話しました。そのロジックに従うと、ES6のほぼすべてが少なくとも1つのブラウザーから欠落しています。私のソリューション(追加されたfuncチェックを使用)は、最も広く使用されているブラウザーでのネイティブカバレッジ、他のすべてのフォールバック、およびNode.jsでの100%カバレッジを提供します。私はあなたがどのエラークラス名を一貫しthis.stack = (new Error(message)).stackてあなたにそれからあなたにそれを得るならば...しかし実際には、これはおそらく大したことではないことに同意します。
Lee Benson

6
これはBabel 6では機能しません:new MyError('foo') instanceof MyError === false
Sukima

5
NPMモジュールとしてbabelでプリコンパイルされたこのコード:extendable-error-class npmjs.com/package/extendable-error-classこれはbabel-plugin-transform-b​​uiltin-extendへの依存を回避するのに便利
brillout

3
this.message = message;冗長であるsuper(message);
mathieug

39

この回答この回答、およびこのコードを組み合わせ、私はこの小さな「ヘルパー」クラスを作成しました。

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で試す


1
this.stack = (new Error(message)).stack;-それ以外の場合、メッセージはスタックトレースから欠落します
Lee Benson

3
これは必要に応じて機能しないのではないかと思います。そうすると、次のようになります。console.log(myerror instanceof ExtendableError); それはまだfalse ..
マウノVähä

4
同じ問題、instanceof CustomErrorを使用しても機能しません。instanceofを使用できない場合に拡張される点は何ですか。
gre 2016

これは、追加向上させることが可能とmessageエラー・スタックのコンストラクタで、そう投げられたときには、スタックの一番上に適切なメッセージを示していますthis.stack = (new Error(message)).stack;
セバスチャン・

1
myerror.name「エラー」を返すようになりました。これが後のバージョンのbabelに関連しているかどうかは不明です

27

ようやくこれを休ませる。Babel 6では、開発者組み込みからの拡張をサポートしていないことは明白です。このトリック、、などのようなものに役立ちませんMapSetで機能しErrorます。例外をスローする可能性がある言語のコアアイデアの1つはカスタムエラーを許可することであるため、これは重要です。プロミスはエラー拒否するように設計されているため、プロミスがより有用になるので、これは二重に重要です。

悲しい真実は、ES2015でこれを古い方法で実行する必要があることです。

Babel REPLの例

カスタムエラーパターン

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

一方、これを可能にするBabel 6のプラグインがあります。

https://www.npmjs.com/package/babel-plugin-transform-b​​uiltin-extend

更新:(2016-09-29現在)いくつかのテストの後、babel.ioがすべてのアサート(カスタム拡張エラーからの拡張)を適切に考慮していないようです。しかし、Ember.JSでは、拡張エラーは期待どおりに機能します。https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce


うん、リンクされたbabelプラグインを使用すると、受け入れられた回答で正しく動作します。(ただし、どうやらリフレクトがないため、結果のファイルはNodeで機能しません。)
KarelBílekMar

好奇心と同じように、ES2016の仕様でビルトインが拡張可能であると述べられている場合、v8のようなVMとBabelのes5がなぜそれに対して翻訳されているのですか プロトタイプチェーンが他のプロトタイプから取得できるのと同じ方法で、クラスがクラスを拡張できることは合理的な期待ではありませんか?なぜプラグインに隠されたそのようなセラミックが必要なのですか?
スキマスイッチ

これは、ほとんどのユースケースが動作を共有する単純なオブジェクトを作成したいだけの場合、特にイライラします。を使用できるカスタムエラーError.toString()。これを達成するために特別なフープと旋回を行う必要があるということは、ほとんどの開発者がそれを回避し、エラーの代わりに文字列をスローするなどの悪い習慣に頼ることを意味します。または、オブジェクトのような独自のマップを作成します。なぜこのようなOOPメソッドを阻止する必要があるのでしょうか。
スキマ

私の意見では、彼らはそれに反対しているのではなく、それは単なる技術的な問題です。私にはわかりません!あなたは彼らに尋ねることができます:)プロジェクトはかなりオープンです
KarelBílek

これまでのところ、このトピックに関する模擬質問からのすべての回答は「バベルはそれをサポートしていない」のままになっています。これで会話の終わりだと思いました。私の牛肉はサポートの欠如が一般的なOOPイディオムを難しくし、ボイラープレートの破片を乗り越えさせるために同僚とさえ戦わなければならなかった。私はここにきれいな代替回避策があったらいいのにと思っています。プラグインを追加するのが最良の選択です。
スキマ

15

編集Typescript 2.1の重大な変更

Error、Array、Mapなどの組み込み関数を拡張すると機能しなくなる可能性があります。

推奨事項として、super(...)呼び出しの直後にプロトタイプを手動で調整できます。

Lee Bensonのオリジナルの回答を編集することは、私には少しうまくいきます。これstackにより、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
あなたは、呼び出す必要がありますObject.setPrototypeOfMyErrorもコンストラクタ。stackoverflow.com/a/41102306/186334 github.com/Microsoft/TypeScript-wiki/blob/master/...
CallMeLaNN

10

babel 6の最新の変更により、transform-b​​uiltin-extendが機能しなくなった。私はこの混合アプローチを使用してしまいました:

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;

そして

import MyError from './MyError';

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

その結果、これらのすべてのテストに合格します。

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

引用

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

電話のthis.stack = (new Error()).stack;おかげでトリックの必要はありませんsuper()

上記のコードは、this.stack = (new Error()).stack;またはBabelError.captureStackTrace(this, this.constructor.name);で呼び出されない限り、スタックトレースを出力できません。IMO、それはおそらくここで1つの問題です。

実際には、スタックトレースは、下出力することができるChrome consoleNode.js v4.2.1、このコードスニペットで。

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

の出力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)

の出力 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回答に加えて、次のようにエラーを定義できます。

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

正しい名前、メッセージ、スタックトレースをスローします:

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
これは機能しません:new MyError('foo') instanceof MyError === false
スキマ

1
それは、上にありNode.js v7.7.3ます。
Gunar Gessner 2017

2

ES6でエラーを拡張しようとしています

そのclass MyError extends Error {…}構文は正しいです。

トランスパイラーは、組み込みオブジェクトからの継承にまだ問題があることに注意してください。あなたの場合、

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

問題を修正するようです。


ほんとだ!しかし、メッセージはとにかく設定されていません-私は新しい例を書きます。
KarelBílek15年

私は今例を書き直しました
KarelBílek2015年


「super(m)」は空のオブジェクトを返すようです。したがって、Object.assignは役に立ちません。
KarelBílek2015年

@KarelBílek:どのブラウザを使用していますか?Error.call()私のために新しいエラーインスタンスを返します。
Bergi、2015年

2

これが受け入れられた回答が機能しなくなった場合、代替手段として常にファクトリを使用できます(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);


必要なbabelプラグインがあれば、受け入れられた回答はまだ機能します。この回答もありがとうございます!
KarelBílek16年

2

私は上記よりも強力な構文を好みます。エラータイプでの追加のメソッドは、あなたがかなりconsole.logまたは何か他のものを作成するのに役立ちます。

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

このコードをテストするには、同様のコードを実行します。

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

    console.warn(message);
}

CustomErrorタイプの拡張は大歓迎です。拡張タイプに特定の機能を追加したり、既存の機能をオーバーライドしたりすることができます。例えば。

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が言及しているように、ネイティブJSを拡張することはできません。OPの質問には答えられません。

Melbourne2991の回答と同様に、私はむしろ工場を使用しましたが、顧客のエラータイプに関するMDNの推奨に従いました。

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

これは私にとってはうまくいきます:

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

0

Babelを使用していませんが、プレーンなES6では、次のように動作します。

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

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

ご覧のとおり、スタックにはエラー名とメッセージの両方が含まれています。何かが足りないのかどうかはわかりませんが、他のすべての答えは複雑すぎるようです。


0

私はこの方法で@Lee Bensonのソリューションを少し改善しました:

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

エラーの例

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 

次に、アプリケーション固有の状況によっては、オプション指定子を使用してエラーをグループ化し、異なる方法で何を行うかを決定できます。

new AuthorizationError ("The user must be a seller to be able to do a discount", AuthorizationError.errors.BAD_PROFILE_TYPE )
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.