1. JavaScript / TypeScript における基本的な定義
まずはベースとなる JavaScript の仕様から。
-
undefined
- 「変数は存在しているが、まだ値が代入されていない状態」を表す。
- 例: 変数を宣言しただけ、関数の戻り値が明示的にない場合。
-
null
- 「明示的に空の値を代入した状態」を表す。
- 例: 「ここには値がない」と開発者が意図して代入する。
let a; // 型は any。値は undefined
let b = undefined; // 値を明示的に undefined に
let c = null; // 値を明示的に null に
2. TypeScript の型システムと --strictNullChecks
TypeScript では --strictNullChecks
オプションを有効にすると、null
と undefined
がそれぞれ別の型として扱われます。
- デフォルト(strictNullChecks: false)
→null
/undefined
が他の型に自由に代入可能 - 厳格モード(strictNullChecks: true)
→string
型の変数にnull
やundefined
は代入不可
→ 必要ならstring | null
やstring | undefined
と Union 型で明示する必要あり
let name: string = "taro";
name = null; // ❌ エラー (strictNullChecks 有効時)
name = undefined; // ❌ エラー
let name2: string | null = null; // ✅ OK
let name3: string | undefined = undefined; // ✅ OK
3. 使い分けの考え方
3.1 undefined
を使うケース
- 「値がまだ設定されていないこと」を自然に表現したい場合
- オプショナルな引数やプロパティのデフォルト値
- ライブラリや API の標準挙動に従う場合(JavaScript の標準は
undefined
)
function greet(name?: string) {
// name が渡されなかったら undefined
console.log("Hello " + (name ?? "Guest"));
}
greet(); // Hello Guest
greet("taro"); // Hello taro
3.2 null
を使うケース
- 「ここには値が存在しない」と 明示したい場合
- データベースや API とのやり取りで「値が存在しない」ことを表現する場合
- 仕様として「空の値をセットしたい」と意図を持たせたい場合
type User = {
id: number;
nickname: string | null; // ニックネームは存在しない場合 null
};
const u1: User = { id: 1, nickname: null };
4. 実務での具体例
4.1 API レスポンス設計
// レスポンス例
{
"id": 1,
"email": "abc@example.com",
"nickname": null // ニックネームが登録されていない
}
- 「まだ送られてこなかった」→
undefined
- 「送られたが空」→
null
4.2 フロントエンドフォーム入力
- 入力が未設定 →
undefined
- 入力が「未入力(空として意図的に送信)」 →
null
4.3 DB とのマッピング
- RDBMS の
NULL
→ TypeScript のnull
- 省略可能な JSON フィールド → TypeScript の
undefined
5. まとめ
-
undefined
→ 「まだ代入されていない」「省略された」 -
null
→ 「値がないと明示した」 - 実務では「仕様の意図」を区別するために
null
を積極的に使う一方、TypeScript/JavaScript の標準挙動に従う場面ではundefined
が自然に発生する。
👉 結論としては、
- オプショナル引数や省略可能プロパティ →
undefined
- 「空の値」を仕様として定義したいとき →
null
これが一番シンプルな使い分けのガイドラインです。