そもそも「nullチェック」って何のためにやるのか
まず一番大事なところから。
nullチェックは「そこに“あるはずのもの”が、本当にあるかを確認する行為」です。
プログラムを書いていると、ついこう思いがちです。
「この値は、まああるでしょ」
「この関数が返す値は、普通 null にはならないはず」
でも現実には、
サーバーからデータが取れなかった
ユーザーが入力してくれなかった
API の仕様が変わった
バグで途中の処理がスキップされた
など、いろんな理由で「あるはずのものがない」が普通に起こります。
その「ないかもしれない」を無視してコードを書くと、
実行中に Cannot read property 'xxx' of null みたいなエラーで落ちます。
TypeScript は、型レベルで「null かもしれないよ」「null をちゃんと扱って」と教えてくれる言語です。
nullチェックの重要性を理解することは、「落ちないコード」「想定外に壊れないコード」を書くための第一歩です。
TypeScript での null の位置づけ(strictNullChecks の世界)
string | null は「文字列かもしれないし、null かもしれない」
TypeScript では、null や undefined は「普通の型」と同じように扱います。
let name: string | null = null;
TypeScriptこれは、「name は string かもしれないし、null かもしれない」という意味です。
ここで name.toUpperCase() と書こうとすると、TypeScript は止めます。
function greet(name: string | null) {
// console.log(name.toUpperCase()); // エラー
}
TypeScriptエラーになる理由はシンプルです。
「name は null かもしれない」
→ 「null に対して toUpperCase は呼べない」
→ 「だから、そのまま呼ぶのは危険ですよ」
と教えてくれているわけです。
つまり、「null かもしれない型」を見たら、null をどう扱うかを“必ず自分で決めてね」と迫られる。
これが TypeScript の世界観です。
具体例で見る:nullチェックをしないと何が起きるか
例:ユーザー名が null になりうるケース
type User = {
id: number;
name: string | null;
};
function printUser(user: User) {
console.log("ユーザー名:", user.name.toUpperCase());
}
TypeScriptこれは TypeScript 的にはエラーです。user.name は string | null なので、そのまま toUpperCase は危険です。
もし強引にコンパイルを通して、実行時に user.name が null だったらどうなるか。
null.toUpperCase() をしようとして落ちます。
これが、よくある「本番で突然クラッシュする」パターンです。
ここで nullチェックを入れると、挙動が明確になります。
function printUser(user: User) {
if (user.name === null) {
console.log("ユーザー名: (未設定)");
} else {
console.log("ユーザー名:", user.name.toUpperCase());
}
}
TypeScriptこれなら、name が null かどうかに応じて、
「どう振る舞うか」がはっきり決まります。
nullチェックとは、
「null が来たときに、どう振る舞う“つもり”なのかを、ちゃんと決める行為」
だとも言えます。
nullチェックで「型が絞り込まれる」ことの重要性
if でチェックすると、その先の世界の型が変わる
TypeScript の嬉しいところは、
nullチェックをすると そのブロックの中だけ型が「非 null」に絞り込まれる ことです。
function printName(name: string | null) {
if (name === null) {
console.log("名前がありません");
return;
}
// ここに来た時点で、name は string 型に絞られている
console.log("こんにちは、" + name.toUpperCase());
}
TypeScript最初 name は string | null です。if (name === null) で null のケースを排除して return したあと、
下の行に来たとき、TypeScript はこう理解します。
「null の場合はさっき return したから、ここまで来ているということは null ではない」
→ 「だからここでは name は string だ」
その結果、name.toUpperCase() が安全に呼べます。
これは設計的にも美しいです。
最初に「異常系(null)」を弾いて
それ以降は「正常系(string)」だけの世界で処理を書く
nullチェックは、「正常な世界」を型レベルで作るための入り口になっています。
nullチェックをサボると、どこが苦しくなるか
「ここは絶対 null じゃないはず」が壊れたとき
よくあるのが、こういう書き方です。
function risky(name: string | null) {
console.log(name!.toUpperCase());
}
TypeScript! は「Non-null assertion operator」で、
「ここでは null じゃないと信じる」という宣言です。
一見便利ですが、これはTypeScript に「黙ってろ」と言っているのと同じです。
もし実際には null が入ってきたら、容赦なく実行時エラーになります。
学習中で一時的に使うのはまだ分かりますが、
本気で「落ちないコード」を書きたいなら、! でごまかすより、“null の場合の振る舞い”をちゃんと決めるほうが圧倒的に健全です。
「null が想定外の場所まで流れ込む」地獄
nullチェックをサボると、
「本来ここでは null はありえないはず」という場所にまで null が伝播します。
すると、
すべての箇所で string | null を意識しなければならない
どこで null がありうるのか、コードを追わないと分からない
という状態になってしまいます。
逆に、
「ここで null を処理しきって、以降は string だけの世界にする」
と決めて if + return を入れておくと、後ろのコードが一気に楽になります。
現実的な「nullチェックの書き方」のパターン
早期 return で「異常系」を先に処理する
一番おすすめの書き方はこれです。
function sendEmail(to: string | null, message: string) {
if (to === null) {
console.log("送信先が指定されていません。メールを送信しません。");
return;
}
// ここから先では to は string 確定
console.log(`送信先: ${to}, メッセージ: ${message}`);
}
TypeScript「null ならここで終わる」というロジックを先頭で書いて、
以降のコードは「正常系だけ」を扱います。
この形にしておくと、
下の方のコードが string 前提で書ける
「null のときどうする?」が関数の最初にまとまる
というメリットがあります。
デフォルト値を用意する
「null のときは、代わりにこの値を使う」という方針なら、?? を使うのもよくあります。
function greet(name: string | null) {
const actualName = name ?? "名無しさん";
console.log("こんにちは、" + actualName);
}
TypeScriptここでは
name が string → そのまま使う
name が null → “名無しさん” に置き換える
というルールを1行で書いています。
この書き方も、ちゃんと「null の場合どうするか」を決めています。
大事なのは、「null を放置しないで、振る舞いを選んでいる」ことです。
「nullチェックの重要性」を自分の言葉にしてみる
ここまでの話を、あえて感覚レベルの言葉にするとこうです。
値が「ないかもしれない」と分かっているのに、それを無視してコードを書くと、
自分の未来の自分(もしくは同僚)が、本番環境で泣くことになる。
TypeScript は、string | null という型を通して、
「本当に null のときどうするつもり?」と、ずっと問いかけてきます。
nullチェックの重要性は、単に「エラーを防ぐ」だけではありません。
「この場面で値がないとき、アプリはどう振る舞うべきか?」
「その振る舞いは、ユーザー体験として自然か?」
そこまで含めて、あなたに決断させてくるものです。
だから、string | null や User | null などの型を見たときは、
「あ、ここは“設計をサボれない場所”なんだ」と一度立ち止まってほしい。
そのうえで、
ここで null を弾いて、後ろを楽にするのか
ここでデフォルト値を当てるのか
ここでエラーとして扱うのか
を、自分でちゃんと選んでいく。
その選択の積み重ねが、「落ちにくくて、読みやすいコード」になっていきます。
nullチェックは、
ただの if 文ではなく、「コードと現実の“欠けうるもの”をつなぐ、小さな誠実さ」だと考えてみてください。
