こんにちは。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
このように定義すると、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の基礎力はすでに十分です。あとは、現場でどんどん試してみてください。何か詰まったら、またいつでも聞きに来てくださいね。応援しています!