TypeScriptのエラーハンドリング入門!初心者でも失敗しない例外処理の基本と重要性
生徒
「TypeScriptでプログラミングをしているときに、画面が真っ白になったり動きが止まったりすることがあります。これってどうすれば防げますか?」
先生
「それはプログラムの中でエラーが発生しているサインですね。TypeScriptではエラーハンドリングという仕組みを使って、問題が起きたときにどう対処するかをあらかじめ決めておくことができます。」
生徒
「エラーハンドリング?難しそうですが、初心者でも使いこなせるようになりますか?」
先生
「大丈夫ですよ!例外処理の基本をマスターすれば、プログラムが途中で止まらない丈夫なアプリが作れるようになります。まずは基本的な考え方から見ていきましょう!」
1. エラーハンドリングとは何か
プログラミングにおけるエラーハンドリングとは、実行中に予期せぬトラブルが起きたときに、プログラムを安全に動かし続けるための技術です。例えば、インターネットの接続が切れてしまったり、入力された文字が想定外のものだったりしたとき、何も対策をしていないとアプリは強制終了してしまいます。これを防ぐために「もし失敗したら、こう動いてね」と指示を出しておくのがエラーハンドリングの役割です。
TypeScriptでは、このエラーのことを例外(Exception)と呼び、その例外を処理することを例外処理といいます。パソコンの操作に慣れていない人でも、銀行の機械で操作を間違えたときに「もう一度入力してください」と案内が出るのを見たことがあるでしょう。あれも一種のエラーハンドリングが働いている状態といえます。プログラムを壊さないために、この知識は欠かせません。
2. try-catch構文の基本的な使い方
TypeScriptでエラーを受け止めるための最も一般的な方法が、try-catch(トライ・キャッチ)という書き方です。名前の通り、まずは処理を「試してみて(try)」、もしエラーが発生したらそれを「捕まえる(catch)」という構造になっています。
以下のサンプルコードを見てみましょう。あえてエラーが発生するような状況を再現しています。
try {
// ここにエラーが起きそうな処理を書きます
console.log("計算を開始します");
// 存在しない関数を呼び出そうとする(エラー発生)
let result = (null as any).toString();
console.log("この行は実行されません");
} catch (error) {
// エラーが起きたときに実行される場所です
console.log("エラーを捕まえましました!");
console.log("原因はこれです:" + error);
}
実行結果は以下のようになります。
計算を開始します
エラーを捕まえましました!
原因はこれです:TypeError: Cannot read properties of null (reading 'toString')
このように、エラーが起きた瞬間に処理がcatchブロックへジャンプするため、プログラム全体が止まることを防いでいます。
3. throwを使って自分でエラーを発生させる
プログラムが勝手にエラーを出すだけでなく、開発者が自分から「これはエラーにしたい!」と宣言することもできます。これにはthrow(スロー)というキーワードを使います。例えば、年齢を入力する欄にマイナスの数字が入ってきたら、それは常識的に考えておかしいですよね。そういった場合にエラーとして投げることができます。
function checkAge(age: number) {
if (age < 0) {
// 自分でエラーを作って投げます
throw new Error("年齢にマイナスの値を入れることはできません");
}
console.log("年齢は正常です:" + age);
}
try {
checkAge(-5);
} catch (e: any) {
console.log("エラーが発生しました:" + e.message);
}
実行結果は以下の通りです。
エラーが発生しました:年齢にマイナスの値を入れることはできません
Errorオブジェクトとは、エラーの内容を詳しく保持するための特別な箱のようなものです。new Error("メッセージ")と書くことで、何が原因でエラーになったのかを文字で残すことができます。
4. finallyで必ず実行したい処理を書く
エラーが起きても起きなくても、最後には必ずやっておきたい処理というものがあります。例えば、ファイルの読み込みを終了したり、通信を閉じたりする場合です。そのために用意されているのがfinally(ファイナリー)ブロックです。tryとcatchの後に付け加えることで、掃除のような後処理を確実に行うことができます。
function processData() {
console.log("処理を開始します");
try {
console.log("データを加工中です...");
// 50%の確率でエラーを出すシミュレーション
if (Math.random() > 0.5) {
throw new Error("加工に失敗しました");
}
} catch (err) {
console.log("エラー通知:" + err);
} finally {
// エラーの有無にかかわらず実行される
console.log("後片付けをして終了します");
}
}
processData();
これを実行すると、成功した場合でも失敗した場合でも、最後には必ず「後片付けをして終了します」というメッセージが表示されます。これは非常に重要な仕組みで、これを使わないと、エラーが起きたときに通信が開いたままになってしまうなどの不具合につながることがあります。
5. TypeScriptにおける型安全なエラー処理
TypeScriptの大きな特徴は、データに「型(かた)」という名前のルールを付けられることです。しかし、実はcatchで捕まえたエラーは、初期状態ではどんな型かわからないunknownやanyという状態になっています。これを安全に扱うためには、型ガードという手法を使います。
型ガードとは、そのデータが本当にエラー(Errorオブジェクト)なのかを確認する作業のことです。instanceofという命令を使うことで、安全にエラーの中身を確認できるようになります。
try {
// 何らかの処理
throw new Error("深刻なトラブル");
} catch (error: unknown) {
// errorが本当にErrorの形をしているかチェック
if (error instanceof Error) {
console.log("メッセージ:" + error.message);
} else {
console.log("未知のエラーが発生しました");
}
}
このように書くことで、プログラムが「このerrorという変数は本当にメッセージを持っているのか?」と迷うことがなくなり、より確実なコードになります。初心者のうちは難しく感じるかもしれませんが、「中身をしっかり確認してから使う」という癖をつけるのが上達の近道です。
6. なぜエラーハンドリングが必要なのか
そもそも、なぜわざわざ手間をかけてエラーを処理しなければならないのでしょうか。その理由は大きく分けて三つあります。
一つ目は、利用者の体験を損なわないためです。もしショッピングサイトでボタンを押した瞬間に画面が固まってしまったら、利用者は不安になりますよね。「ただいま混雑しています」といった適切なメッセージを出すためには、エラーを捕まえておく必要があります。
二つ目は、不具合の原因を見つけやすくするためです。どこでどんなエラーが起きたかをログ(記録)に残しておくことで、後からプログラマーが修正する際の手がかりになります。
三つ目は、予期せぬ動作を防ぐためです。エラーが起きたまま無理やり処理を続けると、データが壊れてしまったり、誤った数値が保存されてしまったりする危険があります。問題が起きたらその場で止める、という勇気もプログラミングには必要なのです。
7. 実践的なエラーハンドリングのコツ
最後に、より良いプログラムを書くためのヒントを紹介します。まず、すべての処理を一つの大きなtry-catchで囲むのは避けましょう。それだと、どこでエラーが起きたのかが分かりにくくなってしまいます。エラーが予想される場所ごとに細かく分けるのがコツです。
また、エラーメッセージには利用者にわかりやすい言葉を使うことも大切です。開発者向けの専門用語(Null Pointerなど)をそのまま出しても、一般のユーザーは何をすればいいかわかりません。「時間を置いてもう一度お試しください」といった親切な案内を心がけましょう。
さらに、TypeScriptの非同期処理(Async/Await)を使う場合も、エラーハンドリングは必須です。インターネットからのデータ取得は失敗する可能性が高いからです。基本的な書き方は同じですので、今のうちにtry-catchの形をしっかり手に覚えさせておきましょう。最初は難しく感じるかもしれませんが、繰り返し書くことで自然と身についていきますよ。