TypeScript | 基礎文法:変数・基本型 – null / undefined

TypeScript
スポンサーリンク

null / undefined は「値がない」を表す特別な2人

まず大枠からいきます。
nullundefined は、どちらも「値がない状態」を表すための特別な値です。ただし、まったく同じ意味ではありません。

ざっくり言うと、

  • undefined は「まだ何も代入されていない」「存在しない」というニュアンス
  • null は「意図的に空っぽにしている」というニュアンス

TypeScriptでは、この2つをちゃんと区別して扱うことがとても大事になります。
なぜかというと、「値がある前提でメソッドを呼んだら、実は null / undefined で落ちた」という事故が、現場で本当に多いからです。


undefined とは何か:まだ何も入っていない「未定」の状態

変数を宣言しただけのときの値

JavaScript / TypeScriptでは、変数を宣言しただけで何も代入しないと、自動的に undefined になります。

let value;
console.log(value); // undefined
TypeScript

ここで value の型は、strict モードかどうかで変わりますが、値としては「まだ何も入っていない」という意味の undefined です。

関数の引数も、渡されなかったときは undefined になります。

function greet(name?: string) {
  console.log(name);
}

greet("Taro"); // "Taro"
greet();       // undefined
TypeScript

name? と書くと「省略可能な引数」になり、渡されなかった場合は undefined になります。

存在しないプロパティを読んだとき

オブジェクトに存在しないプロパティを読もうとしたときも、undefined になります。

const user = { name: "Taro" };

console.log(user.name);   // "Taro"
console.log(user.age);    // undefined
TypeScript

age プロパティは定義されていないので、「値がない」という意味で undefined が返ってきます。


null とは何か:意図的に「空です」と示す値

「ここには今、何もない」と明示する

null は、「ここには今、値がない」と意図的に示すための値です。
たとえば、「ログイン中のユーザー」を表す変数を考えてみます。

let currentUser: string | null = null; // 最初は誰もログインしていない

currentUser = "taro";  // ログインした
currentUser = null;    // ログアウトした
TypeScript

ここで null は、「ログインユーザーが存在しない状態」を明示的に表しています。
undefined だと「そもそも変数が初期化されていないのか、意図的に空なのか」が曖昧になりますが、null を使うと「ここは空っぽにしている」という意思がはっきりします。

APIのレスポンスなどでもよく使われる

外部APIのレスポンスでも、「値がないことを表す」のに null が使われることが多いです。

type User = {
  id: number;
  name: string;
  nickname: string | null; // ニックネームはないかもしれない
};
TypeScript

この場合、nickname は「文字列かもしれないし、nullかもしれない」という意味になります。
「プロパティ自体がない」のではなく、「プロパティはあるけど中身は空」という状態を表現したいときに、null はとても便利です。


TypeScriptにおける null / undefined と strictNullChecks

strictNullChecks がオフの世界(ゆるい世界)

tsconfig.jsonstrictNullChecks がオフ(strict モードを使っていない)だと、nullundefined は多くの型に暗黙的に代入できてしまいます。

let name: string = "Taro";
name = null;       // コンパイルOK(strictNullChecks: false の場合)
name = undefined;  // これもOK
TypeScript

一見便利ですが、「文字列のつもりでメソッドを呼んだら、実は null で落ちた」という事故が起きやすくなります。

let name: string = null;
console.log(name.toUpperCase()); // 実行時にエラー
TypeScript

コンパイルは通るのに、実行時にクラッシュする——TypeScriptが本来防ぎたいパターンです。

strictNullChecks がオンの世界(本気の安全運転)

strictNullChecks: true(あるいは strict: true)にすると、nullundefined は「特別な型」として扱われ、勝手に他の型に混ざれなくなります。

let name: string = "Taro";

// name = null;      // コンパイルエラー
// name = undefined; // コンパイルエラー
TypeScript

「文字列か null かもしれない」なら、型としてもそう書く必要があります。

let name: string | null = null;

name = "Taro"; // OK
name = null;   // OK
// name = undefined; // エラー(string | null には undefined は含まれていない)
TypeScript

こうすることで、「この変数は null かもしれない」という事実が型に刻まれ、使う側もそれを意識せざるを得なくなります。


null / undefined を含む型をどう扱うか

使う前にチェックする

string | null のような型の値を使うときは、「null じゃないことを確認してから使う」というスタイルになります。

let name: string | null = getUserName(); // 返り値が null かもしれない関数

if (name !== null) {
  console.log(name.toUpperCase());
} else {
  console.log("名前が設定されていません");
}
TypeScript

ここで name は、if の中では string として扱われ、else の中では null として扱われます。
TypeScriptの「型の絞り込み(ナローイング)」が効いていて、コンパイラもそれを理解してくれます。

オプショナルチェーンや null合体演算子

少し先の話ですが、?.?? といった演算子を使うと、null / undefined を扱うコードを短く書けます。

const length = name?.length;      // name が null なら length は undefined
const displayName = name ?? "ゲスト"; // name が null / undefined なら "ゲスト"
TypeScript

これらも結局、「null / undefined かもしれない」という前提を型で表現しているからこそ、安全に使える道具です。


初心者が一番ハマりやすい「null / undefined 由来のクラッシュ」

「ある前提」でメソッドを呼んでしまう

典型的な事故パターンはこれです。

type User = {
  name: string | null;
};

const user: User = { name: null };

console.log(user.name.toUpperCase()); // 実行時エラー
TypeScript

namenull かもしれないのに、toUpperCase をそのまま呼んでしまっています。
strictNullChecks がオンなら、コンパイル時にこう怒られます。

オブジェクトは ‘null’ である可能性があります。

これをちゃんと受け止めて、

if (user.name !== null) {
  console.log(user.name.toUpperCase());
}
TypeScript

のように書き直す癖をつけると、「null 由来のクラッシュ」はかなり減ります。


実務的な感覚:null と undefined をどう使い分けるか

「どちらか一方に寄せる」方が読みやすい

現場では、nullundefined を適当に混ぜると、だいたいカオスになります。
なので、プロジェクトとして「基本は null を使う」「基本は undefined を使う」といった方針を決めることが多いです。

たとえば、

  • 「存在しないプロパティ」は自然に undefined になる
  • 「意図的に空を表したいとき」は null を使う

というように、役割を分けるのがひとつのやり方です。

TypeScript的には、「型にちゃんと | null| undefined を書く」「strictNullChecks をオンにする」ことが何より大事です。
そうすれば、「ここは本当に null / undefined を許しているのか?」が型から読み取れるようになります。


まとめ:null / undefined は「怖い」けど、ちゃんと飼いならせる

null と undefined は、どちらも「値がない」を表す特別な存在です。
放っておくと、「ある前提で触ったら実はなかった」というクラッシュの原因になります。

だからこそ、TypeScriptでは、

  • strictNullChecks をオンにして、勝手に混ざらないようにする
  • string | null のように、「かもしれない」を型で表現する
  • 使う前にチェックする、あるいは ?.?? を使う

というスタイルがとても重要になります。

「ここ、本当に null / undefined を許していい場所?」
「許すなら、使う側はちゃんとそれを意識できている?」

この問いを、コードを書きながら自分に投げられるようになると、あなたのTypeScriptは一気に「事故りにくいコード」になっていきます。

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