TypeScriptで独自エラークラスを定義する方法!エラーハンドリングを初心者向けに徹底解説
生徒
「プログラムを実行しているときに、原因がよくわからないエラーが出て困っています。もっと分かりやすくエラーの内容を伝える方法はありますか?」
先生
「TypeScriptには、自分専用のエラーメッセージを作れる『独自エラークラス』という仕組みがあります。これを使えば、何が原因でエラーが起きたのかを明確に分類できるんですよ。」
生徒
「自分専用のエラーですか!難しそうですが、私にも作れますか?」
先生
「大丈夫です!書き方の決まりさえ覚えれば、初心者の方でも簡単に作れます。一緒に学んでいきましょう!」
1. エラーハンドリングと独自エラーとは?
プログラミングの世界では、予期せぬトラブルが発生することを「例外(エラー)」と呼びます。例えば、インターネットに繋がっていないのにデータを取得しようとしたり、数字が入るべき場所に文字が入っていたりする場合です。これらのトラブルに対して、プログラムが止まらないように対策を立てることをエラーハンドリングと言います。
通常、TypeScriptやJavaScriptには標準のエラーが用意されていますが、アプリを作っていると「これはパスワードが間違っているときのエラー」「これは在庫がないときのエラー」という風に、自分たちでエラーに名前をつけたい場面が出てきます。このように、自分で定義したエラー専用の設計図を独自エラークラスと呼びます。
エラーを細かく分けることで、どこで問題が起きたのかがすぐに分かり、修正作業がとても楽になります。パソコンの操作に慣れていない方でも、まずは「エラーを整理整頓するための道具」だと考えてみてください。
2. クラスの継承という仕組みを理解しよう
独自エラーを作るためには、まず「クラス」と「継承(けいしょう)」という言葉を知る必要があります。クラスとは、プログラム内での「設計図」のようなものです。例えば「犬」という設計図があれば、それをもとに「柴犬」や「プードル」を作ることができます。
継承とは、すでにある設計図の機能をそのまま引き継いで、新しい設計図を作ることです。TypeScriptには最初からErrorという名前の設計図が用意されています。独自エラーを作るときは、この標準のErrorという設計図を継承して、自分なりのカスタマイズを加えていきます。
継承を使うことで、標準のエラーが持っている「エラーが起きた場所を記録する」といった便利な機能をそのまま使いつつ、新しい機能や名前を追加できるようになります。これが独自エラー作成の第一歩です。
3. 独自エラークラスの基本的な書き方
それでは、実際にコードを書いて独自エラークラスを定義してみましょう。ここでは、入力されたデータが足りないときに使う「データ不足エラー」を作ってみます。クラスを定義するときはclassというキーワードを使い、継承するときはextends(エクステンズ)を使います。
class MyDataError extends Error {
constructor(message: string) {
super(message); // 親クラスの機能を呼び出す
this.name = "MyDataError"; // エラーに名前をつける
}
}
// 実際に使ってみる
const error = new MyDataError("名前が入力されていません!");
console.log(error.name);
console.log(error.message);
上記のコードで登場するconstructor(コンストラクタ)は、新しいエラーが作られた瞬間に動く特別な処理です。また、super(スーパー)は、継承元の親クラスにある機能を呼び出すための合言葉です。これらを書くことで、標準のエラーと同じように扱うことができるようになります。実行結果は以下のようになります。
MyDataError
名前が入力されていません!
4. 複数の独自エラーを使い分けるメリット
なぜ、わざわざ自分でエラーを作るのでしょうか。それは、エラーの種類によって「画面にメッセージを出すのか」「管理者に通知するのか」といった処理を切り替えられるからです。例えば、銀行のアプリで「残高不足エラー」と「暗証番号間違いエラー」が同じ扱いだったら困りますよね。
独自エラーを定義しておけば、後述するinstanceof(インスタンスオブ)という機能を使って、どのエラーが起きたのかを正確に見分けることができます。これにより、ユーザーにとって親切なメッセージを表示できるようになります。
初心者のうちは、すべてのエラーを一つにまとめてしまいがちですが、アプリの規模が大きくなるにつれて、この「細かく分ける」という作業が非常に重要になってきます。今のうちに、整理整頓の癖をつけておきましょう。
5. try-catch文で独自エラーを捕まえる方法
エラーが発生する可能性がある場所では、try(トライ)とcatch(キャッチ)という構文を使います。「エラーが起きるかもしれないけれど、とりあえずやってみる(try)」、もしエラーが起きたら「そのエラーを捕まえて処理する(catch)」という意味です。ここで、先ほど作った独自エラーが活躍します。
class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = "ValidationError";
}
}
function checkAge(age: number) {
if (age < 0) {
throw new ValidationError("年齢にマイナスの数値は使えません!");
}
console.log("年齢は正常です");
}
try {
checkAge(-5);
} catch (e) {
if (e instanceof ValidationError) {
console.log("バリデーションエラーが発生しました:" + e.message);
} else {
console.log("予期せぬエラーが発生しました");
}
}
throw(スロー)というキーワードは、意図的にエラーを投げるときに使います。ボールを投げるようにエラーを投げると、それをcatchが受け取ります。instanceofを使うことで、「投げられたエラーがValidationErrorかどうか」を判別しています。実行結果を見てみましょう。
バリデーションエラーが発生しました:年齢にマイナスの数値は使えません!
6. 独自エラーに追加の情報を保存する
独自エラーの便利なところは、メッセージ以外にも自由に情報を追加できる点です。例えば、「何番のデータでエラーが起きたのか」というID番号や、「いつエラーが起きたのか」という時刻をエラーの中に保存しておくことができます。
class DatabaseError extends Error {
public errorCode: number;
constructor(message: string, code: number) {
super(message);
this.name = "DatabaseError";
this.errorCode = code; // エラーコードを保存
}
}
try {
throw new DatabaseError("接続に失敗しました", 500);
} catch (e) {
if (e instanceof DatabaseError) {
console.log("名前: " + e.name);
console.log("内容: " + e.message);
console.log("コード: " + e.errorCode);
}
}
このように、public errorCode: numberのように変数を定義することで、エラーに関する詳細なデータを持たせることができます。これにより、デバッグ(プログラムの不具合探し)が非常にスムーズになります。実行結果は以下の通りです。
名前: DatabaseError
内容: 接続に失敗しました
コード: 500
7. TypeScript特有の注意点と便利な書き方
TypeScriptで独自エラーを作るとき、古いバージョンの設定によっては少し特殊な挙動をすることがあります。具体的には、instanceofが正しく判定されない場合があるのです。これを確実に防ぐためには、Object.setPrototypeOf(オブジェクト・セットプロトタイプオブ)という魔法の言葉を一行追加するのがプロの技です。
class SecureError extends Error {
constructor(message: string) {
super(message);
this.name = "SecureError";
// TypeScriptで継承を確実にするためのおまじない
Object.setPrototypeOf(this, SecureError.prototype);
}
}
const myErr = new SecureError("セキュリティ警告です");
console.log(myErr instanceof SecureError);
このObject.setPrototypeOfは、「私の親は確かにSecureErrorですよ」と改めて証明書を発行するようなイメージです。初心者の方は「こういうおまじないが必要なんだな」と覚えておくだけで十分です。これによって、どんな環境でも正しくエラーを判別できるようになります。実行結果は真実を指すtrueが表示されます。
true
8. 実践的なエラーハンドリングの考え方
最後に、エラーハンドリングを行う際の考え方についてお話しします。プログラムを書くときは「すべてが完璧に動く」と信じるのではなく、「どこかで必ず失敗する」と考えて作ることが大切です。これを防御的プログラミングと呼ぶこともあります。
独自エラークラスを定義することは、単にコードを増やすことではなく、未来の自分や一緒に開発する仲間への「手紙」のようなものです。「ここでエラーが起きたら、こういう理由なんだよ」と明確に伝えることで、トラブルが起きたときの不安を減らすことができます。パソコン操作が苦手な方でも、日常の「整理整頓」や「メモ書き」と同じだと考えれば、その重要性が伝わるはずです。
TypeScriptの強力な型チェック機能と、今回学んだ独自エラーを組み合わせることで、非常に壊れにくい頑丈なプログラムを作ることができるようになります。まずは小さなエラークラスから作って、実際にthrowしてcatchする感覚を掴んでみてください。少しずつ、プログラミングが楽しくなっていくはずですよ!