JavaScript 逆引き集 | カスタムエラー投げる

JavaScript JavaScript
スポンサーリンク

カスタムエラー投げるの基本 — throw new Error(‘msg’)

「想定外の入力」「外部APIの不整合」「ビジネスルール違反」など、続行できない状況を即座に知らせたいときは、エラーを投げて処理を止めます。メッセージだけでなく“エラーの種類”や“追加情報”を持たせると、原因の特定と復旧が早くなります。


基本の使い方と標準エラーの選び方

function divide(a, b) {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new TypeError("引数は数値である必要があります");
  }
  if (b === 0) throw new RangeError("0で割ることはできません");
  return a / b;
}
JavaScript
  • 標準エラーの使い分け:
    • Error: 一般的な失敗(種類不明)。
    • TypeError: 型やインターフェイスが不正。
    • RangeError: 範囲や上限が不正。
    • SyntaxError/ReferenceError: 構文や未定義参照(通常はランタイムが投げる)。
  • メッセージ: 何がダメかが一目で分かる短い文にする。

カスタムエラークラスの作成(意味を持たせる)

class ValidationError extends Error {
  constructor(message, field, options) {
    super(message, options);      // ES2022以降は { cause } を渡せる
    this.name = "ValidationError";
    this.field = field;           // どのフィールドが不正か
  }
}

function createUser(input) {
  if (!input.name) {
    throw new ValidationError("nameは必須です", "name");
  }
  return { id: 1, ...input };
}
JavaScript
  • ポイント: extends Error で種類を明確化し、name と追加プロパティ(field, code, status など)を持たせる。
  • 原因の連鎖(cause): 下層エラーを包んでコンテキストを残す。
class ExternalAPIError extends Error {
  constructor(message, { code, status, cause } = {}) {
    super(message, { cause });
    this.name = "ExternalAPIError";
    this.code = code;
    this.status = status; // HTTPステータスなど
  }
}
JavaScript

実務でよく使う投げ方のパターン

  • ガード節で早期中断:
function process(order) {
  if (!order) throw new Error("orderがありません");
  if (!Array.isArray(order.items)) throw new TypeError("itemsは配列です");
  // 続行可能なら以降へ
}
JavaScript
  • 原因を包んで再スロー(context追加):
async function loadProfile(id) {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (err) {
    throw new ExternalAPIError("プロフィール取得に失敗しました", {
      status: err.status,
      cause: err,
    });
  }
}
JavaScript
  • ビジネスルール違反を区別:
class BusinessRuleError extends Error {
  constructor(message, code) {
    super(message);
    this.name = "BusinessRuleError";
    this.code = code; // "NOT_ENOUGH_STOCK" など
  }
}
JavaScript

すぐ使えるテンプレート集

  • assert(条件NGなら投げる)
function assert(cond, msg = "Assertion failed") {
  if (!cond) throw new Error(msg);
}
// 使用例
assert(typeof x === "string", "xは文字列である必要があります");
JavaScript
  • validate(フィールド別にエラー)
function requireField(obj, key) {
  if (obj?.[key] == null) throw new ValidationError(`${key}は必須です`, key);
}
JavaScript
  • 統一メッセージのエラーファクトリ
const Errors = {
  notFound: (entity, id) => new Error(`${entity}(${id})が見つかりません`),
  unauthorized: () => new Error("認証が必要です"),
};
throw Errors.notFound("User", 42);
JavaScript
  • causeでラップして文脈保持
try {
  JSON.parse(badText);
} catch (err) {
  throw new Error("設定ファイルの読み込みに失敗", { cause: err });
}
JavaScript

よくある落とし穴と対策

  • catchで握りつぶす: 何事もなかったように進むと原因不明。
    • 対策: ログ出力・ユーザー通知・必要なら再スローを徹底。
  • 抽象的なメッセージ: 「失敗しました」だけでは診断不能。
    • 対策: 何が、どの値が、どう不正かを具体的に。
  • 種類の未区別: すべて Error だと分岐しづらい。
    • 対策: TypeError/RangeError/カスタムエラーで区別。
  • 原因の喪失: ラップ時に元エラーを捨てる。
    • 対策: cause や追加プロパティでコンテキストを保持。

練習問題(手を動かして覚える)

// 1) 型と範囲のチェックで適切な標準エラーを投げる
function toPercent(n) {
  if (typeof n !== "number") throw new TypeError("数値が必要");
  if (n < 0 || n > 1) throw new RangeError("0〜1の範囲で指定");
  return `${Math.round(n * 100)}%`;
}

// 2) ValidationErrorを作ってフィールド名を添える
class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}
function saveUser(u) {
  if (!u?.email) throw new ValidationError("emailは必須", "email");
  return { ok: true };
}

// 3) 下層エラーをcauseで包んで再スロー
try {
  JSON.parse("{bad: json}");
} catch (e) {
  throw new Error("設定の読み込みに失敗", { cause: e });
}

// 4) エラー種別で分岐
try {
  toPercent("x");
} catch (e) {
  if (e instanceof TypeError) console.log("型を確認してください");
  else console.log("その他のエラー:", e.message);
}
JavaScript

直感的な指針

  • 続行不能なら投げる。種類を適切に選ぶ。
  • カスタムエラーで文脈(field/code/status)を持たせる。
  • 原因は cause やプロパティで失わない。
  • 投げるときは短く具体的に、扱うときは確実にログ・通知・分岐。
タイトルとURLをコピーしました