【実務・中級編】 nullとundefinedの基本型との関係 – TypeScript実践ガイド

TypeScriptの「null」と「undefined」:沼にハマらないための型定義戦略

フロントエンド開発の現場で、バグの温床になりやすいのが「値が空である可能性」のハンドリングです。特にJavaScriptからTypeScriptに移行したばかりのエンジニアが、`strictNullChecks`という巨大な壁に直面して「なぜ`string`に`null`を代入できないんだ!」と頭を抱える姿を、私はこれまで何度も見てきました。

今日は、TypeScriptの型システムにおける`null`と`undefined`の扱いについて、現場レベルの「泥臭い知見」を共有します。

1. `strictNullChecks`という「最強のガードレール」

まず大前提として、実務では`tsconfig.json`の`strictNullChecks`は必ず`true`にしてください。これがないTypeScriptは、ブレーキのないスポーツカーです。

`strictNullChecks: true`の世界では、`null`や`undefined`は「それ専用の型」として扱われます。例えば、`string`型の変数に`null`を代入しようとすると、コンパイラは即座に停止します。これは「予期せぬ実行時エラー(Cannot read property ‘x’ of undefined)」を開発段階で徹底的に叩き潰すための、我々にとって最強の武器なのです。

2. ブラウザの裏側とTypeScriptの型認識

ブラウザのJavaScriptエンジン(V8など)は、`null`と`undefined`を明確に区別します。`undefined`は「変数が宣言されたが値が未定義」という状態、`null`は「意図的に値が空である」状態を指します。

TypeScriptはこの区別を型レベルで厳密にトレースします。ここが重要ですが、「基本型(string, numberなど)」と「空(null, undefined)」を混ぜるには、ユニオン型(`|`)を使って明示的に許容する必要があります。

// 実務で頻出する「APIレスポンスの型定義」の例
interface UserProfile {
id: number;
// ユーザーが未設定の場合を考慮し、stringまたはnullとして定義
bio: string | null;
// 任意プロパティ(省略可能)は型としてundefinedを含む
phoneNumber?: string;
}

const user: UserProfile = {
id: 1,
bio: null, // 明示的な空
// phoneNumberは省略可能(undefinedとして扱われる)
};

// 型ガードによる安全なアクセス
if (user.bio !== null) {
// このブロック内では bio は string 型として推論される
console.log(user.bio.toUpperCase());
}

3. 実践Tips:`null`と`undefined`の使い分け

現場では、この2つを以下のように使い分けるのが「綺麗なコード」の秘訣です。

  • `undefined`: 「まだ値がセットされていない」「プロパティが存在しない」というシステム的な欠如に使う。
  • `null`: 「明確に値が存在しない」というビジネスロジック上の空値として使う。

また、`any`は論外ですが、型が全く不明なデータを受け取る際は`unknown`を使うのが現代の鉄則です。`unknown`は「何者か分からないが、型チェックを通過させないと何もさせない」という最強の防壁です。

4. 現場ですぐ使える「空判定」のベストプラクティス

値が`null`や`undefined`である可能性がある場合、安易に`if (value)`で判定するのは危険です。`0`や`””`(空文字)まで弾いてしまうからです。

以下のユーティリティ関数をプロジェクトの`utils`に置いておくと、チームのコード品質が一段上がります。

/

  • null または undefined を厳密に判定する型ガード

/
export const isPresent = (value: T | null | undefined): value is T => {
return value !== null && value !== undefined;
};

// 使用例:配列の中の空を除去しつつ、型を確定させる
const rawData = [1, null, 2, undefined, 3];
const cleanData = rawData.filter(isPresent); // number[] として推論される

console.log(cleanData); // [1, 2, 3]

最後に:型は「守り」ではなく「攻め」

初級者のうちは「型エラーが出ないようにする」のがゴールになりがちですが、中級者以上の視点では、「型定義によって、どこまでが正常系で、どこからが異常系かを言語化する」ことがゴールになります。

`null`や`undefined`を恐れるのではなく、それらを型システムの中に美しく組み込み、コンパイラをあなたの強力な相棒にしてください。そうすれば、深夜のデバッグ作業から解放され、より本質的なロジックの設計に時間を割けるようになるはずです。

何か分からないことがあれば、いつでもコードを見せに来てください。一緒に最高のアーキテクチャを作り上げましょう。

コメント

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