第31章:null/undefined整理②(安全な書き方)🛟🧷
ねらい🎯
null/undefinedが混ざっても落ちない書き方を身につける🧯✨- 「とりあえず
!」を卒業して、安全に読みやすく直せるようになる🎓🧷 - “デフォルト値”と“異常扱い”を使い分けて、仕様ブレを減らす🧭💡
参考:現行の最新は TypeScript 5.9 系(npm / 公式Downloadページで “currently 5.9”)で、6.0 は 7.0 への橋渡しとして案内されています。(TypeScript)
今日のゴール✅
この章が終わったら、次ができるようになるよ🌸
?.と??を使って、安全アクセス+デフォルトが書ける🧷🛟- 「無いなら無いでOK」なのか「無いのはエラー」なのかをコードで表現できる🚦✨
- 配列・辞書・オプショナルプロパティ周りの “うっかりundefined” を減らせる🧺🧹

まず覚える!安全な3点セット🧰✨
① オプショナルチェーン ?.(無いならそこで止まる)⛔️➡️🫧
a?.b/a?.[0]/a?.()みたいに書けるよ🙂aがnull/undefinedなら、例外を投げずにundefinedになる🛟 (MDN Web Docs)
② Null合体 ??(“0”や“空文字”は尊重する)🫶🔢
- 左が
nullまたはundefinedのときだけ右を使うよ🧷 ||と違って、0や""を「無い」扱いしないのがポイント✨ (MDN Web Docs)
③ ガード節(早期return)🚦✨
「無いなら進まない」を最初に書いて、ネストも事故も減らす💪
- 例:
if (user == null) return ...;(nullとundefined両方をまとめて判定できるよ)🧠
パターン集(コピペしてOK)📚💕
パターンA:安全アクセスだけしたい(落ちたくない)🧷
const city = user?.address?.city; // string | undefined
- “無ければ undefined” が自然な場面で強い🫧
パターンB:安全アクセス+デフォルト値(UI表示でよく使う)🖼️✨
const displayName = user?.profile?.nickname ?? "ゲスト";
nickname = ""(空文字)を「本人が空にした」扱いしたいなら??が正解👌
パターンC:|| を ?? に直して “0事故” を防ぐ🔧🛟
**ビフォー(地味にバグる)**😵
const timeoutMs = config.timeoutMs || 3000; // timeoutMs=0 が潰れる
**アフター(仕様どおり)**😊
const timeoutMs = config.timeoutMs ?? 3000;
||は「falsy(0/""/false)」も右に行くから注意⚠️ (MDN Web Docs)
パターンD:デフォルト値を“代入”したい ??=(設定系に便利)🧰✨
type Options = { retries?: number | null };
function normalize(o: Options) {
o.retries ??= 3; // null/undefined のときだけ 3 を入れる
return o;
}
パターンE:「無いのはエラー」なら、最初に止める🚨
「無いのに進める」より、分かりやすいエラーにする方が安全なことも多いよ🧯
function requireUser(user: { id: string } | null | undefined) {
if (user == null) {
throw new Error("user is required");
}
return user; // ここから user は必ず存在
}
パターンF:!(non-null assertion)は“最後の手段”にする🙅♀️
user!.profile!.nickname! // ← これ、実行時は普通に落ちる可能性ある😭
!は「型だけ黙らせる」ので、事故が実行時に来る😱- まずは ガード節か
?.??で直すのが基本🧷
例題:落ちやすいコードを安全化しよう🛠️💕
お題:クーポン割引を表示したい🎟️✨
「ユーザーにクーポンがあれば割引率を、無ければ 0% を出したい」
**ビフォー(落ちる&バグりやすい)**💥
type Coupon = { discountPercent?: number | null };
type User = { coupons?: Coupon[] | null };
export function getDiscountPercent(user: User | null) {
const first = user!.coupons![0]; // 💥 user/coupons が無いと落ちる
return first.discountPercent || 0; // 😵 0% が “無い” 扱いになる
}
**アフター(落ちない&仕様どおり)**🛟✨
type Coupon = { discountPercent?: number | null };
type User = { coupons?: Coupon[] | null };
export function getDiscountPercent(user: User | null): number {
const first = user?.coupons?.at(0); // 無ければ undefined
return first?.discountPercent ?? 0; // null/undefined だけ 0
}
ここが超大事ポイント💡
- 「クーポンが無ければ 0%」=正常系のデフォルトだから
?? 0が自然🌸 - 「絶対クーポンが必要」な仕様なら、上のパターンEみたいに throw を選ぶ方が安全🚨
“無い”をどう扱う?ミニ方針(迷ったらこれ)🧭✨

1) 表示・UI:無いならそれっぽく埋める🖼️
?? "未設定"/?? 0/?? []が多いよ😊
2) ビジネスルール:無いなら止める🚨
- 期待してる入力が無いなら、早めに throw / return で止める
- バグの位置が分かりやすくなる🧯
tsconfigで “うっかりundefined” を早めに潰す🧷👮♀️
特に効く3つ⚡(プロジェクトが許すならON推奨)
strictNullChecks:null/undefinedをちゃんと型で扱う🧷noUncheckedIndexedAccess:arr[i]やobj[key]に| undefinedを足してくれる(取りこぼし減る)🛟exactOptionalPropertyTypes:x?: Tとx?: T | undefinedを区別してくれて、曖昧さが減る🎯
公式のフラグ説明(TSConfig / compiler options)にも載ってるよ📘(TypeScript)
手順(小さく刻む)👣🛟
-
落ちる場所を特定(例:
Cannot read properties of undefined)🔍 -
その場所に「無いならどうする?」を決める🧭
- デフォルトにする?(
??) - そこで終了?(ガード節)
- エラーにする?(throw)
- デフォルトにする?(
-
!を外して、?./??/ ガード節で置き換える🧷✨ -
型チェック&テストで確認✅🧪
-
“同じ悩み”が他にもないか検索して、同パターンでまとめて直す🔎🧹
ミニ課題✍️🌸(15〜25分)
課題1:|| を ?? に直して仕様バグを消す🧯
次のコードを、0 や "" が潰れないように直してね🙂
export function normalize(input: { retries?: number; label?: string }) {
const retries = input.retries || 3;
const label = input.label || "default";
return { retries, label };
}
課題2:ガード節でネストを減らす🚦✨
export function getFirstTag(user?: { tags?: string[] }) {
if (user) {
if (user.tags) {
if (user.tags.length > 0) {
return user.tags[0];
}
}
}
return "no-tag";
}
- “早期return” でスッキリさせてね👣✨
課題3:! を卒業する🎓🧷
type Profile = { nickname?: string | null };
export function hello(p?: Profile) {
return "Hi " + p!.nickname!.toUpperCase();
}
- 落ちない形にして、
nicknameが無い時は"Hi guest"にしてみよう😊
AI活用ポイント🤖✅(お願い方+チェック観点)
便利なお願い方(そのまま投げてOK)📝
- 「この関数、
null/undefinedで落ちる可能性を列挙して、安全化パターンを3案ください。!は使わないで」🤖🛟 - 「
||を使ってるデフォルト設定を、??に直すべき箇所がある?理由も」🤖🔍 - 「このコードをガード節でリファクタして。挙動は変えないで、差分は小さく」🤖👣
AIの提案を採用する前のチェック✅
0/""/falseの意味が潰れてない?🔢🫧- “無い”を
throwにすべき仕様じゃない?🚨 ?.の結果がundefinedになって、その後で落ちない?(toUpperCase()とか)💥- 型がちゃんと絞れてる?(
if (x == null) return;の後はxが非null)🧷✨
まとめ🧁✨
- 落ちない基本形は
?.+??+ ガード節🚦🛟 - デフォルトにするか、止めるか、エラーにするかを先に決めると迷わない🧭
||は便利だけど “0/空文字” を潰すので、デフォルト戦略は??が安心🫶(MDN Web Docs)