TypeScriptでnull・undefined・optionalエラーを防ぐ!安全なコードの書き方完全ガイド
生徒
「プログラムを実行すると、データがないよというエラーが出て止まってしまいます。どうすればいいですか?」
先生
「それは、プログラミングの世界で最も多いエラーの一つですね。TypeScriptを使えば、データが空っぽの状態を事前に予測して対策できますよ。」
生徒
「データが空っぽの状態って、nullやundefinedのことですよね?具体的にどう防ぐんですか?」
先生
「はい、その通りです。今回は、初心者の方でも絶対に失敗しない、安全なデータの扱い方を詳しく解説します!」
1. nullとundefinedとは何かを理解しよう
プログラミングを始めたばかりの方が最初につまずくのが、null(ヌル)とundefined(アンデファインド)という概念です。これらはどちらも「値が存在しない」という状態を表しますが、ニュアンスが少し異なります。
例えば、空の箱を想像してみてください。undefinedは「箱そのものがまだ用意されていない、あるいは中身が何かも決まっていない状態」です。一方でnullは「箱は用意されているけれど、意図的に中身を空にしている状態」を指します。パソコンの操作で例えるなら、ファイルを作成すらしていないのがundefined、ファイルは作ったけれど中身が空っぽなのがnullというイメージです。
TypeScriptという言語の最大の特徴は、これらの「空っぽの状態」をプログラムが動く前にチェックしてくれる点にあります。これにより、本番のアプリで急にエラーが出て止まってしまうという最悪の事態を防ぐことができるのです。
2. 型定義で空の状態を許容する方法
TypeScriptでは、変数にどのような種類のデータを入れるかを決める「型」というルールがあります。通常、数値型(number)と決めた変数には数値しか入れられません。しかし、現実のプログラムでは「最初は空だけど、後からデータが入る」という場面が多々あります。
そのような場合は、ユニオン型という仕組みを使います。これは、複数の型を「または」という記号(|)でつなぐ書き方です。例えば「数値またはnull」という指定をすることで、安全に空の状態を許可できます。
// 数値か、あるいは空(null)が入る可能性がある変数
let userScore: number | null = null;
// 後から数値を代入するのはOK
userScore = 100;
console.log(userScore);
このコードでは、最初にnullを入れてもエラーになりません。もしこの指定をせずにnullを入れようとすると、TypeScriptが「数値を入れる約束なのに空っぽを入れるのはダメだよ」と教えてくれます。これがエラーを未然に防ぐ第一歩です。
3. オプショナルプロパティで柔軟なデータ構造を作る
次に紹介するのはオプショナルプロパティです。これは、オブジェクト(複数のデータをまとめたもの)の中で、「あってもなくても良い項目」を作る機能です。項目の名前の後にハテナマーク(?)をつけるだけで使えます。
例えば、会員登録の情報を考えてみましょう。名前は必須ですが、趣味の項目は入力してもしなくても良い場合があります。このような場合にオプショナルプロパティが活躍します。これを使わないと、趣味を入力していない人全員がエラーになってしまいますが、オプショナルプロパティなら柔軟に対応可能です。
// 会員情報のルールを決める
interface Member {
name: string;
age?: number; // ハテナをつけると、あってもなくても良くなる
}
// 年齢を書かなくてもエラーにならない
const member1: Member = {
name: "田中太郎"
};
console.log(member1.name);
このように、データが存在しない可能性をあらかじめ考慮して設計することで、システムが予期せぬ停止を起こすリスクを大幅に減らすことができます。初心者のうちは、何でも必須にするのではなく「これは空になる可能性があるかな?」と考える癖をつけるのが上達のコツです。
4. オプショナルチェイニングで安全に値にアクセスする
データの中にさらにデータが入っているような複雑な構造を扱う際、中身が空なのに無理やり中身を見ようとすると「TypeError: Cannot read property...」という有名なエラーが発生します。これを防ぐ魔法の書き方がオプショナルチェイニングです。
ドット(.)の前にハテナ(?)をつけて「?.」と書くことで、「もし中身があれば進む、なければそこで止まってundefinedを返す」という処理を一行で書けます。昔のプログラミングでは、一つひとつif文で中身があるか確認していましたが、今はこれだけで解決します。
const user = {
contact: {
email: "example@test.com"
}
};
// contactがあるか不明な場合でも安全にメールを読み込む
// もしcontactがなければ、エラーにならずにundefinedが返る
const userEmail = user.contact?.email;
console.log(userEmail);
この書き方を覚えるだけで、実行時のエラーは劇的に減ります。特に外部からデータを取得するような、何が返ってくるか分からない不安定な状況で非常に強力な武器になります。
5. Null合体演算子でデフォルト値を設定する
データが空だったときに、そのまま空として扱うのではなく「代わりの値(デフォルト値)」を表示したいことがあります。そのときに便利なのがNull合体演算子(??)です。ハテナを二つ並べて書きます。
これは「左側の値がnullかundefinedだったら、右側の値を使う」という命令です。例えば、ユーザーがニックネームを設定していない場合に「ゲストさん」と表示するような処理が簡単に作れます。if文を何行も書く必要がなくなり、コードがとてもスッキリします。
let inputNickname: string | null = null;
// 名前がなければ「名無しさん」という文字を入れる
const displayName = inputNickname ?? "名無しさん";
console.log(displayName);
名無しさん
この機能は、設定画面の初期値や、検索結果がゼロだったときのメッセージ表示など、Webサイト制作のあらゆる場面で利用されます。ユーザーに不親切な「null」という文字を見せないためにも、欠かせないテクニックです。
6. 非破壊的なエラーハンドリングの重要性
エラーハンドリングとは、問題が起きたときにプログラムを強制終了させず、適切に対処することを指します。TypeScriptでは「例外を投げる(throw)」という方法もありますが、最近は「エラーをデータとして返す」という手法が推奨されることも多いです。
例えば、割り算をするプログラムで「0で割る」という操作は数学的にできません。このとき、アプリをエラーで落とすのではなく、「計算に失敗しました」というメッセージを返せば、ユーザーは操作を続けることができます。これを実現するためには、これまで学んだnullや特定の型を組み合わせて、安全な出口を作ってあげることが大切です。
エラーを防ぐことは、単にプログラムを動かすこと以上に重要です。それは、そのアプリを使うユーザーの体験を守ることだからです。TypeScriptの機能を活用して、エラーが起きても優しく対応できる仕組みを構築しましょう。
7. 型ガードを使って安全性を確実にする
最後に紹介するのは型ガードという考え方です。これは「今扱っているデータが確実に特定の型であること」を証明する作業です。例えば、データが「文字列かnull」の状態のとき、そのままでは文字列として操作(文字数を数えるなど)はできません。なぜなら、nullかもしれないからです。
そこでif文を使って「もしこれがnullでないなら」という条件を書きます。すると、そのif文の中では、TypeScriptは「あ、これはもう確実に文字列だね」と認識してくれます。これを型を絞り込むと呼びます。地味な作業に見えますが、これがもっとも確実で強力なエラー対策になります。
function printMessage(msg: string | null) {
if (msg !== null) {
// ここではmsgは確実にstring型として扱える
console.log("メッセージの長さは: " + msg.length);
} else {
console.log("メッセージは空っぽです");
}
}
printMessage("こんにちは");
printMessage(null);
このように、データが不確実な状態を放置せず、一歩ずつ確認しながら処理を進めるのがプロフェッショナルの書き方です。TypeScriptは、あなたが確認を忘れていると警告を出してくれる頼もしい相棒のような存在です。最初は面倒に感じるかもしれませんが、慣れてくるとこれほど心強い味方はいないことに気づくはずです。