null / undefined は「値がない」を表す特別な2人
まず大枠からいきます。null と undefined は、どちらも「値がない状態」を表すための特別な値です。ただし、まったく同じ意味ではありません。
ざっくり言うと、
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
TypeScriptname? と書くと「省略可能な引数」になり、渡されなかった場合は undefined になります。
存在しないプロパティを読んだとき
オブジェクトに存在しないプロパティを読もうとしたときも、undefined になります。
const user = { name: "Taro" };
console.log(user.name); // "Taro"
console.log(user.age); // undefined
TypeScriptage プロパティは定義されていないので、「値がない」という意味で 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.json で strictNullChecks がオフ(strict モードを使っていない)だと、null や undefined は多くの型に暗黙的に代入できてしまいます。
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)にすると、null と undefined は「特別な型」として扱われ、勝手に他の型に混ざれなくなります。
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()); // 実行時エラー
TypeScriptname が null かもしれないのに、toUpperCase をそのまま呼んでしまっています。
strictNullChecks がオンなら、コンパイル時にこう怒られます。
オブジェクトは ‘null’ である可能性があります。
これをちゃんと受け止めて、
if (user.name !== null) {
console.log(user.name.toUpperCase());
}
TypeScriptのように書き直す癖をつけると、「null 由来のクラッシュ」はかなり減ります。
実務的な感覚:null と undefined をどう使い分けるか
「どちらか一方に寄せる」方が読みやすい
現場では、null と undefined を適当に混ぜると、だいたいカオスになります。
なので、プロジェクトとして「基本は null を使う」「基本は undefined を使う」といった方針を決めることが多いです。
たとえば、
- 「存在しないプロパティ」は自然に
undefinedになる - 「意図的に空を表したいとき」は
nullを使う
というように、役割を分けるのがひとつのやり方です。
TypeScript的には、「型にちゃんと | null や | undefined を書く」「strictNullChecks をオンにする」ことが何より大事です。
そうすれば、「ここは本当に null / undefined を許しているのか?」が型から読み取れるようになります。
まとめ:null / undefined は「怖い」けど、ちゃんと飼いならせる
null と undefined は、どちらも「値がない」を表す特別な存在です。
放っておくと、「ある前提で触ったら実はなかった」というクラッシュの原因になります。
だからこそ、TypeScriptでは、
- strictNullChecks をオンにして、勝手に混ざらないようにする
string | nullのように、「かもしれない」を型で表現する- 使う前にチェックする、あるいは
?.や??を使う
というスタイルがとても重要になります。
「ここ、本当に null / undefined を許していい場所?」
「許すなら、使う側はちゃんとそれを意識できている?」
この問いを、コードを書きながら自分に投げられるようになると、あなたのTypeScriptは一気に「事故りにくいコード」になっていきます。
