【TypeScript】nullとundefinedの安全な扱い方(strictNullChecks)

JavaScript

こんにちは、かつコーチです。

前回は、オプショナルプロパティ(?)と readonly の使い方について解説しました。

今回は、TypeScriptを実務で使ううえで避けて通れない「nullundefined の扱い方」を取り上げます。

「Cannot read properties of undefined」というエラーメッセージを見たことがある人は多いと思いますが、TypeScriptの strictNullChecks を理解すると、このエラーの多くをコンパイル時点で防げるようになります。

strictNullChecksとは?

null・undefinedを許容するかどうかの設定

strictNullChecks は、TypeScriptのコンパイラオプションのひとつです。

tsconfig.json で次のように設定します。

{
  "compilerOptions": {
    "strict": true
  }
}

strict: true を指定すると、strictNullChecks を含む複数の厳格なチェックがまとめて有効になります。

このオプションがオフの場合、TypeScriptはどんな型の変数にも nullundefined を代入できてしまいます。

// strictNullChecksがオフの場合
let name: string = null; // エラーにならない

オンにすると、明示的に許可しない限り nullundefined を代入できなくなります。

// strictNullChecksがオンの場合
let name: string = null; // ❌ エラー:Type 'null' is not assignable to type 'string'

なぜstrictNullChecksが重要なのか

JavaScriptの実行時エラーの中でも、特に多いのが「値がないのにあるものとして扱ってしまう」ことによるエラーです。

const user = getUser(); // ユーザーが見つからずnullが返ってくる可能性がある
console.log(user.name); // TypeError: Cannot read properties of null

strictNullChecks をオンにしておくと、こういった「値がない可能性」をコンパイラが検出し、実行前に気づかせてくれます。

私が新卒の頃、strictNullChecks をオフのまま開発を続けていたプロジェクトで、本番環境で undefined のプロパティを読もうとしてサービスが落ちたことがありました。

その一件以来、新しいプロジェクトを立ち上げるときは必ず strict: true を最初から有効にするようにしています。

null許容型の書き方

ユニオン型でnullやundefinedを表現する

strictNullChecks がオンの状態で「この値は null になる可能性がある」と伝えたい場合は、ユニオン型を使います。

function findUser(id: number): string | null {
  if (id === 1) {
    return "かつコーチ";
  }
  return null;
}

const name = findUser(2);
console.log(name.length); // ❌ エラー:nullの可能性があるため直接使えない

string | null という型にすることで、「文字列が返ってくることもあれば、見つからずに null が返ってくることもある」という関数の意図を型として明示できます。

このままではエラーになるため、使う側で null かどうかをチェックする必要があります。

const name = findUser(2);

if (name !== null) {
  console.log(name.length); // ここまで来ればstring型として扱える
} else {
  console.log("ユーザーが見つかりませんでした");
}

このように、条件分岐によって型が絞り込まれる仕組みを「型ガード」と呼びます。

Before/Afterで見るnullチェックの書き方

チェックを省略してエラーになるパターン

❌ Before:nullチェックを省略してしまう

interface User {
  id: number;
  name: string;
  address: string | null;
}

function printAddressLength(user: User) {
  console.log(user.address.length); // ❌ エラー:addressはnullの可能性がある
}

addressstring | null 型なので、null の場合に .length を呼ぶとランタイムエラーになります。

TypeScriptはこの危険性をコンパイル時点で検出し、そのままではコンパイルを通しません。

✅ After:条件分岐で安全にチェックしてから使う

interface User {
  id: number;
  name: string;
  address: string | null;
}

function printAddressLength(user: User) {
  if (user.address !== null) {
    console.log(user.address.length); // string型として扱える
  } else {
    console.log("住所は未登録です");
  }
}

条件分岐を挟むことで、TypeScriptが「このブロックの中では addressstring として扱ってよい」と判断してくれます。

オプショナルチェイニングとNull合体演算子

?.(オプショナルチェイニング)

プロパティを安全に辿りたいときは、?.(オプショナルチェイニング)が便利です。

interface User {
  profile: {
    bio: string;
  } | null;
}

function printBio(user: User) {
  console.log(user.profile?.bio); // profileがnullならundefinedを返す(エラーにならない)
}

user.profilenull の場合、そこで処理を止めて undefined を返してくれるため、if 文でいちいちチェックしなくても安全にアクセスできます。

??(Null合体演算子)

nullundefined だった場合に、代わりのデフォルト値を設定したいときは ?? を使います。

function getDisplayName(name: string | null): string {
  return name ?? "ゲスト";
}

console.log(getDisplayName("かつコーチ")); // "かつコーチ"
console.log(getDisplayName(null));         // "ゲスト"

似たような目的で使われる ||(論理OR)との違いに注意が必要です。

const count = 0;

console.log(count || 10); // 10(0はfalsyなので10になってしまう)
console.log(count ?? 10); // 0(nullでもundefinedでもないので0のまま)

||0 や空文字のような「falsyな値」もすべて置き換えてしまいますが、??nullundefined の場合だけ置き換えます。

私はこの違いに気づかず、「在庫数0件」を表示したいのに || を使ったせいでデフォルト値が表示されてしまう、という不具合を出してしまったことがあります。

nullundefined かどうかだけを判定したい」場合は、?? を選ぶのが安全です。

nullとundefinedの使い分け方

実務でのおすすめの使い分け

TypeScriptでは nullundefined の両方を扱えますが、実務では使い分けの指針を決めておくとチーム全体でコードが読みやすくなります。

状況おすすめの値理由
プロパティが「まだ設定されていない」undefined(またはオプショナル ?JavaScriptの初期値の慣習に近い
「意図的に空である」ことを示したいnull「値がない」ことを明示的に表現できる
APIから返ってくる「見つからなかった」結果null多くのAPI設計・DB設計の慣習に合わせやすい

どちらか一方に統一するチームも多く、たとえば「関数の戻り値が見つからない場合は必ず null」「オブジェクトの未設定プロパティは undefined」のようにルールを決めておくと、型の見通しがよくなります。

まとめ

この記事のポイント

  • strictNullChecksstrict: true)を有効にすると、nullundefined の扱いをコンパイル時にチェックできる
  • null を許容する値は string | null のようにユニオン型で明示する
  • 条件分岐(型ガード)を使うと、null でないことが保証された状態で値を扱える
  • ?.(オプショナルチェイニング)と ??(Null合体演算子)を使うと安全に値を扱いやすい
  • ??nullundefined のみを、|| はfalsy全般を置き換える点に注意する

次に読むべき記事

次回は、TypeScriptの型システムの応用への橋渡しとなる「ジェネリクス」の入門編を解説します。

→ 次の記事:ジェネリクス入門:型を引数のように扱う

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