Closed14

[Node.js] アプリケーションレイヤーでカスタムエラーを定義する「うまみ」が知りたい

Taisei.MTaisei.M

0→1の開発を行っているプロジェクトでエラーハンドリングの設計を考え中
Errorインスタンスを拡張したカスタムエラーを使用する方法を紹介している記事が多いけどなんでそうしないといけないんだっけの整理

Taisei.MTaisei.M

そもそもなんでカスタムエラーを定義しないといけないんだっけの調査

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Error

Taisei.MTaisei.M

言語レベルだと、Errorインスタンスを拡張したカスタムエラーを使ったエラー処理は普通にやってることっぽい
例えば、↓はカスタムエラーとしてErrorインスタンスを継承してる

  • EvalError
  • RangeError
  • TypeError
Taisei.MTaisei.M

そもそもthrow はいろんな例外を投げれるのにErrorインスタンスを継承したやつじゃないとダメな理由ってなんだ?

Taisei.MTaisei.M

例えば、こんなふうに実装しても動作的にはthrowされた例外がcatchで受け取られるから問題はなさそう

function getRectArea(width, height) {
  if (isNaN(width) || isNaN(height)) {
    // new Errorではなく、単にオブジェクトをthrowする
    throw {errorCode: 10001, message: 'hogehoge'};
  }
}

try {
  getRectArea(3, "A");
} catch (e) {
  console.error(e);
  // 実際の出力: Object { errorCode: 10001, message: "hogehoge" }
}
Taisei.MTaisei.M

この記事で書かれているように、throwでなんでも投げれるけどErrorインスタンスを通すほうがいいよね(わかりやすいよね)ってことなのか?

JavaScriptでは任意の値を例外としてthrowすることができますが、実際にはErrorのインスタンスをthrowするのが慣例です。

https://www.wantedly.com/companies/wantedly/post_articles/492456

Taisei.MTaisei.M

In practice, the exception you throw should always be an Error object or an instance of an Error subclass, such as RangeError. This is because code that catches the error may expect certain properties, such as message, to be present on the caught value. For example, web APIs typically throw DOMException instances, which inherit from Error.prototype.

いや、慣例というよりMDNでも「実際にthrowする例外は、Errorオブジェクトかそれを継承したカスタムエラーであるべき」って書かれてる

Taisei.MTaisei.M

throwで投げる例外にはErrorインスタンスを利用すべきとなっているのであれば、カスタムエラーを定義するのは自然な流れなのかも

Taisei.MTaisei.M

instance ofでカスタムエラーかどうかを判定して処理を書けることはうまみのひとつ

JavaScript は任意の引数で throw できるので、技術的にはカスタムのエラークラスは Error から継承する必要はありません。しかし、継承しているとエラーオブジェクトを識別する obj instanceof Error を使えるようになります。そのため、継承しておくほうのがベターです。

https://ja.javascript.info/custom-errors

Taisei.MTaisei.M

エラーを分類しつつ実装者が考えないといけないことを減らせる点もうまみポイント
例外をthrowする際はすでに定義されているカスタムエラーを呼び出すだけだから、nameどうしようとかを考える必要がない

https://zenn.dev/dhik/articles/15990b37324baa

Taisei.MTaisei.M

当然ながらカスタムプロパティを自由に定義できるのも良き

もしコンストラクタを再定義するときは、cause引数を意識した定義にするのをおすすめします。以下はlocというカスタムプロパティを持つエラークラスを定義する例です。

class ParseError extends Error {
  static {
    this.prototype.name = "ParseError";
  }
  constructor(message = "", options = {}) {
    const { loc, ...rest } = options;
    // causeがあるときはErrorに渡される
    super(message, rest);
    this.loc = loc;
  }
}

https://www.wantedly.com/companies/wantedly/post_articles/492456

このスクラップは4ヶ月前にクローズされました