TypeScript | 基礎文法:関数の基礎 – nullチェックの重要性

TypeScript
スポンサーリンク

そもそも「nullチェック」って何のためにやるのか

まず一番大事なところから。
nullチェックは「そこに“あるはずのもの”が、本当にあるかを確認する行為」です。

プログラムを書いていると、ついこう思いがちです。

「この値は、まああるでしょ」
「この関数が返す値は、普通 null にはならないはず」

でも現実には、

サーバーからデータが取れなかった
ユーザーが入力してくれなかった
API の仕様が変わった
バグで途中の処理がスキップされた

など、いろんな理由で「あるはずのものがない」が普通に起こります。
その「ないかもしれない」を無視してコードを書くと、
実行中に Cannot read property 'xxx' of null みたいなエラーで落ちます。

TypeScript は、型レベルで「null かもしれないよ」「null をちゃんと扱って」と教えてくれる言語です。
nullチェックの重要性を理解することは、「落ちないコード」「想定外に壊れないコード」を書くための第一歩です。


TypeScript での null の位置づけ(strictNullChecks の世界)

string | null は「文字列かもしれないし、null かもしれない」

TypeScript では、nullundefined は「普通の型」と同じように扱います。

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.namestring | null なので、そのまま toUpperCase は危険です。

もし強引にコンパイルを通して、実行時に user.namenull だったらどうなるか。

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

最初 namestring | 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 | nullUser | null などの型を見たときは、
「あ、ここは“設計をサボれない場所”なんだ」と一度立ち止まってほしい

そのうえで、

ここで null を弾いて、後ろを楽にするのか
ここでデフォルト値を当てるのか
ここでエラーとして扱うのか

を、自分でちゃんと選んでいく。
その選択の積み重ねが、「落ちにくくて、読みやすいコード」になっていきます。

nullチェックは、
ただの if 文ではなく、「コードと現実の“欠けうるもの”をつなぐ、小さな誠実さ」だと考えてみてください。

タイトルとURLをコピーしました