TypeScriptでバリデーションエラーの型を定義する方法!初心者向けデータ検証ガイド
生徒
「TypeScriptで画面から入力されたデータが正しいかチェックするときに、エラーの内容をきれいに型として管理する方法はありますか?」
先生
「TypeScriptでは、バリデーションエラー専用の型を自分で定義して、どの項目でどんなエラーが起きたかを安全に扱うことができますよ。」
生徒
「エラーの形をあらかじめ決めておけると、プログラムがバグを起こしにくくなりそうですね!具体的にはどのように書くのですか?」
先生
「それでは、データ検証の基本からエラーの型定義のやり方まで、一つずつ順番に見ていきましょう!」
1. バリデーションと型定義の基本とは
プログラミングの世界におけるバリデーションとは、入力されたデータが正しい形式かどうかをチェックする検証作業のことです。例えば、インターネットの会員登録画面で、メールアドレスの欄に文字が入っていなかったり、年齢の欄にマイナスの数字が入力されたりしたときに、「正しく入力してください」と警告を出す仕組みのことです。
パソコンを触ったことがない方に向けて身近な例えを出すと、役所の書類提出に似ています。書類の氏名欄や住所欄が空欄のまま窓口に出すと、係の人から「ここが空欄になっていますよ」と指摘されます。この確認作業こそがバリデーションです。
TypeScriptというプログラミング言語では、このバリデーションを行う際に型という仕組みを使います。型とは、データを入れる箱の種類やルールのことです。文字を入れるための箱、数字を入れるための箱というように、あらかじめルールを決めておくことで、間違ったデータが紛れ込むのを防ぐことができます。
2. なぜエラーに型を定義する必要があるのか
バリデーションを行うプログラムを作るとき、ただ「エラーがあります」とだけ結果を返すと、プログラムの他の部分で「どの項目が原因でエラーになったのか」が分からなくなってしまいます。名前が未入力なのか、それともパスワードが短すぎるのか、原因がはっきりしないと、画面に適切な案内を表示することができません。
そこで、エラーそのものにも型を定義して、どのような形式でエラー情報が戻ってくるかを明確にします。エラーの形をしっかりと定義しておくことで、開発中にパソコンの画面上で「このエラー情報には、名前に関するメッセージが含まれていますよ」ということをTypeScriptが教えてくれるようになります。これにより、記述ミスによる不具合を未然に防ぐことが可能になります。
3. 最もシンプルなエラー型を定義してみよう
まずは、一番簡単で分かりやすいエラーの型定義を考えてみましょう。ここでは、文字の入力チェックを行うシンプルなプログラムを作成します。関数という、特定の処理をまとめた命令の塊を使って、名前が正しく入力されているかを検証します。
以下のプログラムでは、エラーの情報として「エラーメッセージの文字列」をそのまま返すような仕組みを作っています。まずは最も基本的な形を確認してみましょう。
function checkName(inputName: string): string | null {
if (inputName === "") {
return "名前が入力されていません。";
}
return null;
}
let result1 = checkName("");
console.log(result1);
上記のプログラムを実行すると、以下のような結果が画面に表示されます。入力された文字が空っぽだったため、エラーの文字が出力されています。
名前が入力されていません。
このプログラムでは、名前が空っぽのときにエラーメッセージという文字を返し、問題がないときには「何もない」ことを意味する「null」という特別な値を返すように型を割り当てています。
4. 複数の項目に対応するオブジェクトのエラー型定義
実際のホームページなどでは、名前だけでなく、メールアドレスや年齢など、たくさんの項目を一度にチェックすることがほとんどです。複数の項目がある場合、エラーメッセージも項目ごとに分けて管理したほうが便利です。そのようなときに使うのが、オブジェクトと呼ばれる複数のデータを一つにまとめる仕組みです。
ここでは、会員登録の入力を想定して、名前とメールアドレスの二つの項目に関するエラーを保持できる型を定義してみましょう。型を定義するときは「type」という命令の言葉を使用します。
type RegistrationErrors = {
username?: string;
email?: string;
};
function validateRegistration(username: string, email: string): RegistrationErrors {
let errors: RegistrationErrors = {};
if (username === "") {
errors.username = "ユーザー名を入力してください。";
}
if (email === "") {
errors.email = "メールアドレスを入力してください。";
}
return errors;
}
let registrationResult = validateRegistration("", "");
console.log(registrationResult);
このプログラムを実行すると、それぞれの項目に対して発生したエラーが、綺麗に整理されて出力されます。
{ username: 'ユーザー名を入力してください。', email: 'メールアドレスを入力してください。' }
型の定義にある「?」という記号は、その項目が「あってもなくてもどちらでも良い」という意味になります。エラーが何も発生しなかった項目は、エラーメッセージを入れなくてよいため、この記号をつけて柔軟に対応できるようにしています。
5. エラーの詳細情報を管理する高度な型定義
さらに本格的なプログラムを作る場合、単にエラーメッセージの文字を保存するだけでなく、「エラーの種類」や「エラーが起きた場所」なども一緒にまとめて管理したくなることがあります。そうすることで、画面のデザインを赤色に変えたり、特定の場所にカーソルを自動で移動させたりといった、親切な動きを実装しやすくなります。
次は、エラーの項目名、エラーの原因、そして具体的なメッセージを一つのセットとして扱い、それを複数並べた一覧として管理する型を定義します。プログラムの世界では、データの並んだ一覧のことを配列と呼びます。
type ValidationErrorDetail = {
field: string;
type: string;
message: string;
};
type DetailedErrors = ValidationErrorDetail[];
function validateAge(ageInput: number): DetailedErrors {
let errorList: DetailedErrors = [];
if (ageInput < 0) {
errorList.push({
field: "age",
type: "invalid_number",
message: "年齢にマイナスの数値を指定することはできません。"
});
}
if (ageInput < 18) {
errorList.push({
field: "age",
type: "underage",
message: "18歳未満の方は登録できません。"
});
}
return errorList;
}
let ageResult = validateAge(-5);
console.log(ageResult);
このプログラムを実行すると、配列という一覧の形式の中に、エラーの詳細なデータが詰め込まれて出力されます。
[
{
field: 'age',
type: 'invalid_number',
message: '年齢にマイナスの数値を指定することはできません。'
},
{ field: 'age', type: 'underage', message: '18歳未満の方は登録できません。' }
]
エラーが発生した場所が「age」であり、原因が「invalid_number」や「underage」であるということが、一目で判別できるようになりました。このように型をしっかりと組み立てることで、複雑な条件チェックも迷わずに管理できるようになります。
6. 成功と失敗を明確に分ける型定義の応用方法
バリデーションを行った結果、データが「成功」したのか、それとも「失敗」してエラーになったのかを、誰が見てもはっきりと区別できるようにするテクニックがあります。これを専門用語で判別可能なユニオン型と呼びますが、難しい言葉は気にしなくて大丈夫です。要するに、「成功の箱」と「失敗の箱」を完全に別物として用意する手法です。
成功したときは検証済みの正しいデータを返し、失敗したときは先ほど定義したようなエラーの型を返すように設定します。これにより、プログラムの安全性が劇的に向上します。
type ValidationSuccess = {
status: "success";
data: string;
};
type ValidationFailure = {
status: "failure";
errorMessage: string;
};
type ValidationResult = ValidationSuccess | ValidationFailure;
function verifyCode(code: string): ValidationResult {
if (code === "1234") {
return {
status: "success",
data: "認証に成功しました。"
};
} else {
return {
status: "failure",
errorMessage: "暗証番号が間違っています。"
};
}
}
let finalResult = verifyCode("0000");
if (finalResult.status === "failure") {
console.log(finalResult.errorMessage);
}
このプログラムを実行すると、状態が失敗であることを判定し、エラーメッセージのみを選んで出力させることができます。
暗証番号が間違っています。
「status」という項目の中身が「success」か「failure」かを確認するだけで、その後にどのデータを使えばよいかがパソコン側で自動的に判断されるようになります。これが、TypeScriptでバリデーションエラーに型を定義して扱う最大のメリットです。初心者の方も、まずは真似をして書いてみることから始めて、データのチェック機能を実装してみてください。