【入門編】引数に渡す「コールバック関数」の非同期エラーハンドリングを型で強制する – TypeScript コア・型システムの基礎解析バイブル

こんにちは。TypeScriptの深淵へようこそ。

現場でコードを書いていて、「非同期処理のコールバック、エラーハンドリングを書き忘れてアプリがクラッシュした」という経験はありませんか?

実は、TypeScriptの型システムを正しく使えば、「エラーハンドリングを強制する」という堅牢な契約(コントラクト)をコードに埋め込むことができます。今回は、コールバックの戻り値を制御することで、呼び出し側に「例外処理を強いる」テクニックを伝授します。

—

なぜ「Promiseを返す関数」は危険なのか?

まずは、よくある失敗例を見てみましょう。

// よくある非同期コールバックの型定義
type Callback = (data: string) => Promise;

async function executeTask(cb: Callback) {
try {
await cb(“Hello TypeScript”);
} catch (e) {
console.error(“エラーが発生しました”, e);
}
}

// 呼び出し側
executeTask(async (data) => {
// ここでエラーが起きても、呼び出し側は関知できないことが多い
throw new Error(“失敗!”);
});

一見良さそうに見えますが、「呼び出し側がエラーを握りつぶしていないか」を型システムは検知できません。私たちは、関数そのものの戻り値に「成功」と「失敗」のパターンを閉じ込める必要があります。

—

「結果型(Resultパターン)」でエラーを型に昇華させる

TypeScriptで最も堅牢なアプローチの一つが、「成功と失敗を内包した型」を作ることです。

// 成功した時の型と、失敗した時の型を定義
type Result =
| { success: true; data: T }
| { success: false; error: E };

// コールバックは必ず「Result」を返すように制約する
type AsyncCallback = (data: string) => Promise>;

このように定義すると、TypeScriptは「戻り値がResult型であること」を強制します。

呼び出し側の強制力

この定義を使うと、IDEの補完と型チェックがあなたを助けてくれます。

async function runner(cb: AsyncCallback) {
const result = await cb(“start”);

// ここで分岐を強制される!
if (!result.success) {
// エラー時の処理を書かないと、result.data にアクセスできない
console.error(“失敗しました:”, result.error.message);
return;
}

// ここでは result.data が確実に存在することが保証される
console.log(“成功:”, result.data);
}

—

初学者が陥りやすい「文法エラー」の罠

ここでよくあるミスを整理しておきましょう。

1. 「戻り値がvoidだとエラーハンドリングが抜ける」

  • `Promise` と書くと、TypeScriptは「成功したことだけ」を期待します。必ず `Result` のような構造体を使って、失敗の可能性を型に含めてください。

2. `any`型で妥協してしまう

  • 「とりあえず動けばいい」と `any` を使うと、せっかくの型システムが沈黙します。TypeScriptの真価は「未知のデータ構造に名前を付けること」にあります。面倒でも型を定義しましょう。

—

現場で役立つ「チェックリスト」

ここをクリアすれば、あなたのコードは劇的に堅牢になります。

  • [ ] Promiseを返す関数は「何が起きるか」を型で表現しているか?
  • `void` を返す関数は、エラー処理が呼び出し側に依存してしまいます。
  • [ ] 戻り値の「型」にエラーケースが含まれているか?
  • `Result` 型や `Either` 型を使い、成功と失敗を明確に区別しましょう。
  • [ ] `if` 文で型の絞り込み(Type Guard)を行っているか?
  • `result.success` を判定することで、TypeScriptは安全なデータのみを抽出してくれます。

—

最後に:型は「守り」ではなく「コミュニケーション」

TypeScriptの型定義は、単なるエラーチェックではありません。「この関数を呼び出す人は、こうやってエラーに対処してください」という設計者から利用者へのメッセージです。

非同期処理のエラーハンドリングを型で強制することは、大規模開発において「バグの温床」を一つずつ潰していく行為そのもの。最初は少し冗長に感じるかもしれませんが、これこそが「伝説的なコード」への第一歩です。

ここまで理解できたなら、あなたのTypeScriptの基礎力はすでに十分です。あとは、現場でどんどん試してみてください。何か詰まったら、またいつでも聞きに来てくださいね。応援しています!

タイトルとURLをコピーしました