2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおけるエラーハンドリング戦略:try-catchを超えた責務分離と構造的エラーフロー設計

Posted at

概要

エラーハンドリングとは「失敗したときに止めること」ではない。
それは**“失敗の型と責任を明確にし、どこで何を補足し、どう伝達し、どう復旧するかを構造的に定義する設計”**である。

JavaScriptは try-catch に頼るだけではなく、返却型(Result)・throwable型・UI伝播型など、多層的なエラーフローが可能。
正しいエラーハンドリングは、層ごとの責務を守り、ユーザーにも開発者にも安心を提供する


1. エラーの責務分離:誰がどこで処理するべきか?

| 層           | 責務                                 |
|--------------|--------------------------------------|
| API/ドメイン | エラーを定義し、throwまたは返却     |
| ユースケース | エラーを分類し、状態やUIに変換       |
| UI層         | 表示制御、再試行、通知               |

2. 明示的なエラー構造の設計(Result型)

type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };

async function fetchUser(id: string): Promise<Result<User, 'NotFound' | 'NetworkError'>> {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) return { ok: false, error: 'NotFound' };
    return { ok: true, value: await res.json() };
  } catch {
    return { ok: false, error: 'NetworkError' };
  }
}
  • throw しない構造 → UIで確実に分岐処理可能
  • ok: false は構造的エラー → try-catch不要

3. 例外型(throw)と返却型(Result)の使い分け

- Result型 → UI/業務処理など **期待される失敗**
- throw型 → 不正入力 / 想定外バグ / 破壊を防ぐ例外
function parseJson(input: string): object {
  try {
    return JSON.parse(input);
  } catch {
    throw new Error('Invalid JSON');
  }
}
  • ✅ 「例外 = 想定外」「Result = 想定済み」という分類設計

4. 非同期エラーとキャンセルの分離制御

try {
  const res = await fetch('/api', { signal: controller.signal });
} catch (e) {
  if (e.name === 'AbortError') return;
  notify('ネットワークエラーが発生しました');
}
  • キャンセル(Abort)とエラーを分離
  • ✅ UIや再試行に必要な情報として整理

5. エラー型の定義と分類戦略

type LoginError =
  | { type: 'InvalidCredentials' }
  | { type: 'TooManyAttempts'; retryAfter: number }
  | { type: 'ServerError'; message: string };
  • ✅ Union型で「エラーの種類」を明示的に管理
  • ✅ UIは type で分岐 → 表示 / 再試行 / リダイレクト など制御

6. UI層でのエラーUI / 再試行 / アラート設計

if (!result.ok) {
  switch (result.error.type) {
    case 'InvalidCredentials':
      showError('パスワードが間違っています');
      break;
    case 'TooManyAttempts':
      showRetry(result.error.retryAfter);
      break;
    default:
      showError('不明なエラーが発生しました');
  }
}
  • ✅ UI側の責務は「通知」「選択肢提示」「体験制御」

設計判断フロー

① そのエラーは「想定済みの失敗」か? → Result型で扱う

② 例外は「不正入力 / バグ / 想定外」か? → throwで構造を破壊

③ 非同期キャンセルと失敗が同一扱いになっていないか?

④ UIは「なぜ失敗したか」を受け取れる構造になっているか?

⑤ 再試行可能性 / ユーザー通知 / UI復帰が実装できる構造か?

よくあるミスと対策

❌ try-catch で全部握って何が起きたかわからない

→ ✅ 想定済みはResult型、例外は分類して構造管理


❌ UIで「エラー」としか表示されない

→ ✅ Union型で分類し、UIに伝える構造に


❌ API失敗で画面が白くなる or 無反応になる

→ ✅ 失敗時の状態反映・通知・再試行UIを用意


結語

エラーハンドリングとは「止める」ことではない。
それは**“失敗を分類し、意味を構造に落とし、層ごとに責務を分けて、安心して壊れるための戦略”**である。

  • Result型とthrow型を設計的に使い分け
  • UIと非同期の整合性を保ち
  • 状態に失敗を織り込み、UXに転化する

JavaScriptにおけるエラーハンドリング戦略とは、
“壊れても制御可能で、意図を持って回復可能な、構造設計の最終防衛線”である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?