- Day 20:Optional・null・undefinedで「ないかもしれない」を安全に扱う
- Optional・null・undefinedの基本を整理する
- Optional Chaining ?.:安全に「あるかもしれないもの」をたどる
- Nullish Coalescing ??:なかったときの「代わり」を用意する
- 実践:プロフィール情報を安全に表示する
- もう少し現実的なプロフィール例を作ってみる
- Day 20 練習テンプレート:Optional・?.・?? をまとめて体験する
- Day 20のまとめ
- Day 20:Optional・null・undefinedで「存在しないかもしれない」を10パターンで体験する
- 基本の考え方を軽くおさらいする
- 10パターンで「存在しないかもしれない」を処理してみる
- Day 20 練習テンプレート:10パターンをまとめて試せるコード
- Day 20のまとめ
Day 20:Optional・null・undefinedで「ないかもしれない」を安全に扱う
第3週は、TypeScriptの核心である「型」をじっくり育てていく週です。 Day 20のテーマは 「存在しないかもしれない値」をどう安全に扱うか です。
キーワードは次の5つです。
?(Optional)nullundefined- Optional Chaining
?. - Nullish Coalescing
??
実践では、プロフィール情報を安全に表示するコードを題材に、 「落ちないコード」「優しい表示」を両立する書き方を身につけていきます。
Optional・null・undefinedの基本を整理する
null と undefined のざっくりイメージ
まずは、null と undefined の違いをざっくり整理します。
undefined- 「まだ値が設定されていない」「存在しない」というニュアンス
- 何も代入していない変数の初期値などでよく出てきます
null- 「値がないことを意図的に表現している」というニュアンス
- 「ここには値が入るはずだけど、今は空ですよ」と明示したいときに使われます
TypeScriptでは、null と undefined をきちんと意識して扱うことで、 「あると思って触ったら実はなかった」という事故を減らしていきます。
Optional(?)とは:あってもなくてもいいプロパティ
? は、「このプロパティはあってもなくてもいいですよ」という意味です。
type Profile = {
nickname?: string; // あってもなくてもよい
};
const p1: Profile = { nickname: "T" }; // OK
const p2: Profile = {}; // OK(nicknameなし)
TypeScriptnickname?: stringは、「nicknameはstringかもしれないし、ないかもしれない」という型です。- Optionalを使うと、「必須ではない情報」を自然に表現できます。
Optional Chaining ?.:安全に「あるかもしれないもの」をたどる
まずは素直なアクセスを書いてみる
今回の例として、次のようなオブジェクトがあります。
const user = {
name: "Taro",
profile: {
nickname: "T",
},
};
TypeScriptふつうに書くと、ニックネームを表示するコードはこうなります。
console.log(user.profile.nickname);
TypeScriptこのコードは、user.profile が必ず存在する前提で書かれています。 しかし、現実のアプリでは「プロフィールがまだ設定されていないユーザー」もいますよね。
例えば、次のようなユーザーがいたとします。
const userWithoutProfile = {
name: "Hanako",
// profile がない
};
TypeScriptこの状態で userWithoutProfile.profile.nickname にアクセスすると、 profile が undefined なので、undefined.nickname を読もうとしてエラーになります。
?. を使って「あるときだけ」安全にアクセスする
そこで登場するのが Optional Chaining ?. です。
console.log(user.profile?.nickname);
TypeScriptuser.profile?.nicknameは、「profileが存在するならnicknameを読む、なければundefined」という意味になります。profileがundefinedのときでも、エラーにならずにundefinedが返ってきます。
これで、「プロフィールがないユーザー」に対しても、コードが落ちずに動き続けてくれます。
Nullish Coalescing ??:なかったときの「代わり」を用意する
?? の基本イメージ
?? は、「左側が null または undefined のときに、右側の値を使う」 演算子です。
const value = undefined;
const result = value ?? "デフォルト";
console.log(result); // "デフォルト"
TypeScript- 左側が
null/undefined→ 右側を採用 - 左側が
0や""のような「値はあるけど中身が空っぽ」なもの → 左側をそのまま採用
ここが、||(論理OR)との大きな違いです。
console.log("" || "fallback"); // "fallback"(空文字はfalse扱い)
console.log("" ?? "fallback"); // ""(null/undefinedではないのでそのまま)
TypeScript?? は、「本当に値がないときだけ代わりを使いたい」という場面でとても便利です。
実践:プロフィール情報を安全に表示する
サンプルコードを分解して理解する
今回の実践コードはこちらです。
const user = {
name: "Taro",
profile: {
nickname: "T",
},
};
console.log(user.profile?.nickname ?? "未設定");
TypeScriptこれをステップバイステップで見ていきます。
user.profile?.nicknameprofileが存在するならnicknameを読むprofileがないならundefinedになる
... ?? "未設定"- 左側が
null/undefinedのとき →"未設定"を使う - 左側にちゃんと値があるとき → その値を使う
- 左側が
つまり、この1行はこういう意味になります。
「プロフィールがあってニックネームが設定されていればそれを表示し、 なければ
"未設定"と表示する」
コメント付きで書き直してみる
const user = {
name: "Taro",
profile: {
nickname: "T",
},
};
// プロフィールがあるかもしれないし、ないかもしれない前提で安全にアクセス
const nicknameToShow =
user.profile?.nickname ?? "未設定";
console.log("ニックネーム:", nicknameToShow);
TypeScript重要ポイント(ここはしっかり深掘りしたいところです):
?.で「途中のオブジェクトがないときでも落ちない」ようにしている??で「本当に値がないときだけ、代わりの表示を用意している」- この2つを組み合わせることで、「安全で、かつユーザーに優しい表示」が実現できます。
もう少し現実的なプロフィール例を作ってみる
Optionalなプロパティを持つ型を定義する
TypeScriptの型も使って、もう少し現実的なプロフィール情報を表現してみます。
type Profile = {
nickname?: string; // あってもなくてもよい
bio?: string; // 自己紹介(任意)
};
type User = {
name: string;
profile?: Profile; // プロフィール自体がない可能性もある
};
const user1: User = {
name: "Taro",
profile: {
nickname: "T",
},
};
const user2: User = {
name: "Hanako",
// profile なし
};
TypeScript安全にプロフィールを表示する関数
function printUserProfile(user: User) {
// ニックネームがあればそれを、なければ「未設定」
const nickname = user.profile?.nickname ?? "未設定";
// 自己紹介があればそれを、なければ「未設定」
const bio = user.profile?.bio ?? "未設定";
console.log("=== プロフィール表示 ===");
console.log("名前:", user.name);
console.log("ニックネーム:", nickname);
console.log("自己紹介:", bio);
}
printUserProfile(user1);
printUserProfile(user2);
TypeScriptポイント:
user.profile?.nicknameのように、profileがない場合も想定して安全にアクセスしています。?? "未設定"で、「本当に値がないときだけ」代わりの文字列を使っています。- Optionalなプロパティと
?./??の組み合わせは、実務でも頻出のパターンです。
Day 20 練習テンプレート:Optional・?.・?? をまとめて体験する
最後に、今日の内容をひとつのファイルにまとめた練習テンプレートを載せます。 このままコピーして、プロパティを増やしたり、表示メッセージを変えたりしながら、 「ないかもしれない」を安全に扱う感覚を育ててみてください。
// day20-optional-null-undefined.ts
// Day 20:Optional・null・undefined・?.・?? 練習テンプレート
type Profile = {
nickname?: string; // 任意のニックネーム
bio?: string; // 任意の自己紹介
};
type User = {
name: string;
profile?: Profile; // プロフィール自体がない可能性もある
};
const user1: User = {
name: "Taro",
profile: {
nickname: "T",
bio: "エンジニアです。",
},
};
const user2: User = {
name: "Hanako",
// プロフィールなし
};
const user3: User = {
name: "Jiro",
profile: {
// nickname なし
bio: "ゲームが好きです。",
},
};
function printUserProfile(user: User) {
// Optional Chaining + Nullish Coalescing で安全に表示
const nickname = user.profile?.nickname ?? "未設定";
const bio = user.profile?.bio ?? "未設定";
console.log("=== プロフィール表示 ===");
console.log("名前:", user.name);
console.log("ニックネーム:", nickname);
console.log("自己紹介:", bio);
console.log("------------------------");
}
// テスト
printUserProfile(user1);
printUserProfile(user2);
printUserProfile(user3);
TypeScriptDay 20のまとめ
Day 20では、
null/undefinedのざっくりした違い?(Optional)で「あるかもしれない」プロパティを表現する方法- Optional Chaining
?.で「途中がないかもしれない」オブジェクトを安全にたどる方法 - Nullish Coalescing
??で「本当に値がないときだけ代わりを使う」書き方 - プロフィール情報を題材に、「安全で優しい表示」を実現するコード
を、具体的な例とともに体験しました。
「ないかもしれない」をちゃんと扱えるようになると、 エラーで落ちないだけでなく、ユーザーにとっても気持ちのよい挙動を作れるようになります。 ぜひ、設定画面やプロフィール画面など、身近な場面を思い浮かべながら、 ?・?.・?? を自分のものにしていってください。
Day 20:Optional・null・undefinedで「存在しないかもしれない」を10パターンで体験する
第3週は、TypeScriptの核心である「型」をじっくり育てていく週です。 Day 20の演習テーマは、「存在しない可能性があるデータ」を10パターン処理してみることです。
キーワードは次の5つです。
?(Optional)nullundefined- Optional Chaining
?. - Nullish Coalescing
??
ここでは、よくあるアプリの場面をイメージしながら、 「あるかもしれない」「ないかもしれない」を安全に、そして読みやすく扱う書き方を身につけていきます。
基本の考え方を軽くおさらいする
Optional(?)とは
? は、「このプロパティはあってもなくてもいいですよ」という意味です。
type Profile = {
nickname?: string; // あってもなくてもよい
};
TypeScriptnickname?: stringは、「stringかもしれないし、そもそもプロパティがないかもしれない」という型です。
Optional Chaining ?. とは
?. は、「途中がないかもしれないけど、あったら先に進む」ための安全なアクセス方法です。
user.profile?.nickname; // profileがなければundefinedになる
TypeScriptNullish Coalescing ?? とは
?? は、「左側がnullかundefinedのときだけ、右側の値を使う」演算子です。
const nickname = user.profile?.nickname ?? "未設定";
TypeScriptnull/undefinedのとき →"未設定"- それ以外(空文字
""や0など) → 左側の値をそのまま使う
この3つを組み合わせると、「存在しないかもしれないデータ」をかなり気持ちよく扱えるようになります。
10パターンで「存在しないかもしれない」を処理してみる
ここからは、よくあるシチュエーションを10個並べて、 それぞれどう書くと安全で読みやすいかを見ていきます。
1. ニックネーム(プロフィールの基本)
type Profile = {
nickname?: string; // 任意のニックネーム
};
type User = {
name: string;
profile?: Profile; // プロフィール自体がない可能性もある
};
function printNickname(user: User) {
// profileがあるかもしれないし、nicknameがないかもしれない
const nickname = user.profile?.nickname ?? "未設定";
console.log("ニックネーム:", nickname);
}
TypeScriptポイント:
profile?.nicknameで「途中がないかもしれない」を安全にたどる?? "未設定"で「本当に値がないときだけ」代わりを表示する
2. 自己紹介文(bio)が未設定かもしれない
function printBio(user: User) {
const bio = user.profile?.bio ?? "自己紹介はまだ書かれていません。";
console.log("自己紹介:", bio);
}
TypeScriptポイント:
- 空文字
""を「書かれていない」とみなしたくない場合、??がちょうどよいです。 - 「null / undefinedだけを特別扱いする」感覚を持っておくと、挙動をイメージしやすくなります。
3. 年齢が未入力かもしれない
type UserWithAge = {
name: string;
age?: number; // 年齢は任意
};
function printAge(user: UserWithAge) {
const ageText = user.age ?? "未入力";
console.log("年齢:", ageText);
}
TypeScriptポイント:
age?: numberは「数値かもしれないし、プロパティ自体がないかもしれない」。?? "未入力"で、「年齢が設定されていない」ことをわかりやすく表示できます。
4. 住所情報が部分的に欠けているかもしれない
type Address = {
prefecture?: string;
city?: string;
detail?: string;
};
type UserWithAddress = {
name: string;
address?: Address;
};
function printAddress(user: UserWithAddress) {
const prefecture = user.address?.prefecture ?? "都道府県未設定";
const city = user.address?.city ?? "市区町村未設定";
const detail = user.address?.detail ?? "番地未設定";
console.log("住所:", `${prefecture} ${city} ${detail}`);
}
TypeScriptポイント:
- 「一部だけ設定されている」ケースも想定して、項目ごとに
??を使うと丁寧です。 - ユーザーにとっても「どこが未設定なのか」がわかりやすくなります。
5. 電話番号リストが存在しないかもしれない
type Contact = {
phones?: string[]; // 電話番号の配列(ないかもしれない)
};
function printPhones(contact: Contact) {
const phones = contact.phones ?? []; // null/undefinedなら空配列にする
if (phones.length === 0) {
console.log("電話番号: 未登録");
} else {
console.log("電話番号一覧:", phones.join(", "));
}
}
TypeScriptポイント:
?? []で「配列がないときは空配列にする」というパターンはよく使います。- その後の処理(
lengthやmapなど)が書きやすくなります。
6. 設定値(テーマ)が未設定かもしれない
type Settings = {
theme?: "light" | "dark";
};
function getTheme(settings: Settings) {
// 未設定なら "light" をデフォルトにする
const theme = settings.theme ?? "light";
console.log("現在のテーマ:", theme);
}
TypeScriptポイント:
- 「未設定ならこの値」というデフォルトを決めておくと、UIの一貫性が保てます。
??は「設定値のデフォルト」を決める場面でとても便利です。
7. 最終ログイン日時がないかもしれない
type UserWithLogin = {
name: string;
lastLoginAt?: Date | null; // nullもありうる
};
function printLastLogin(user: UserWithLogin) {
const lastLoginText =
user.lastLoginAt?.toISOString() ?? "ログイン履歴なし";
console.log("最終ログイン:", lastLoginText);
}
TypeScriptポイント:
lastLoginAtがnullの場合も??の対象になります。- 「まだ一度もログインしていない」という状態を、明示的に表現できます。
8. カートの割引情報がないかもしれない
type Discount = {
rate?: number; // 割引率(0.1なら10%)
};
type Cart = {
total: number;
discount?: Discount;
};
function printCartTotal(cart: Cart) {
const rate = cart.discount?.rate ?? 0; // 割引がなければ0%
const discountedTotal = cart.total * (1 - rate);
console.log("元の合計:", cart.total);
console.log("割引後の合計:", discountedTotal);
}
TypeScriptポイント:
- 「割引がないときは0%」というビジネスルールを、
?? 0で型に落とし込んでいます。 - こうしておくと、後からロジックを読む人にも意図が伝わりやすいです。
9. 会社情報が未登録かもしれない
type Company = {
name: string;
department?: string;
};
type UserWithCompany = {
name: string;
company?: Company;
};
function printCompany(user: UserWithCompany) {
const companyName = user.company?.name ?? "会社未登録";
const department = user.company?.department ?? "部署未設定";
console.log("会社名:", companyName);
console.log("部署:", department);
}
TypeScriptポイント:
company自体がない場合と、departmentだけがない場合を、自然に区別できています。- Optionalな構造が深くなっても、
?.と??の組み合わせでシンプルに書けます。
10. SNSリンクが一部だけ存在するかもしれない
type SocialLinks = {
twitter?: string;
github?: string;
website?: string;
};
function printSocialLinks(links: SocialLinks) {
const twitter = links.twitter ?? "未登録";
const github = links.github ?? "未登録";
const website = links.website ?? "未登録";
console.log("Twitter:", twitter);
console.log("GitHub:", github);
console.log("Website:", website);
}
TypeScriptポイント:
- 「どのSNSが登録されていて、どれが未登録か」を明確に表示できます。
- ユーザーにとっても、「どこを編集すればいいか」がわかりやすくなります。
Day 20 練習テンプレート:10パターンをまとめて試せるコード
最後に、ここまでの10パターンをひとつのファイルにまとめたテンプレートを載せます。 そのままコピーして、値を変えたり、パターンを増やしたりしながら、 「存在しないかもしれない」を扱う感覚を体に馴染ませていってください。
// day20-optional-10patterns.ts
// Day 20:「存在しない可能性があるデータ」を10パターン処理する
// 1. ニックネーム
type Profile = {
nickname?: string;
bio?: string;
};
type User = {
name: string;
profile?: Profile;
};
function printNickname(user: User) {
const nickname = user.profile?.nickname ?? "未設定";
console.log("ニックネーム:", nickname);
}
// 2. 自己紹介
function printBio(user: User) {
const bio = user.profile?.bio ?? "自己紹介はまだ書かれていません。";
console.log("自己紹介:", bio);
}
// 3. 年齢
type UserWithAge = {
name: string;
age?: number;
};
function printAge(user: UserWithAge) {
const ageText = user.age ?? "未入力";
console.log("年齢:", ageText);
}
// 4. 住所
type Address = {
prefecture?: string;
city?: string;
detail?: string;
};
type UserWithAddress = {
name: string;
address?: Address;
};
function printAddress(user: UserWithAddress) {
const prefecture = user.address?.prefecture ?? "都道府県未設定";
const city = user.address?.city ?? "市区町村未設定";
const detail = user.address?.detail ?? "番地未設定";
console.log("住所:", `${prefecture} ${city} ${detail}`);
}
// 5. 電話番号
type Contact = {
phones?: string[];
};
function printPhones(contact: Contact) {
const phones = contact.phones ?? [];
if (phones.length === 0) {
console.log("電話番号: 未登録");
} else {
console.log("電話番号一覧:", phones.join(", "));
}
}
// 6. テーマ設定
type Settings = {
theme?: "light" | "dark";
};
function getTheme(settings: Settings) {
const theme = settings.theme ?? "light";
console.log("現在のテーマ:", theme);
}
// 7. 最終ログイン
type UserWithLogin = {
name: string;
lastLoginAt?: Date | null;
};
function printLastLogin(user: UserWithLogin) {
const lastLoginText =
user.lastLoginAt?.toISOString() ?? "ログイン履歴なし";
console.log("最終ログイン:", lastLoginText);
}
// 8. カート割引
type Discount = {
rate?: number;
};
type Cart = {
total: number;
discount?: Discount;
};
function printCartTotal(cart: Cart) {
const rate = cart.discount?.rate ?? 0;
const discountedTotal = cart.total * (1 - rate);
console.log("元の合計:", cart.total);
console.log("割引後の合計:", discountedTotal);
}
// 9. 会社情報
type Company = {
name: string;
department?: string;
};
type UserWithCompany = {
name: string;
company?: Company;
};
function printCompany(user: UserWithCompany) {
const companyName = user.company?.name ?? "会社未登録";
const department = user.company?.department ?? "部署未設定";
console.log("会社名:", companyName);
console.log("部署:", department);
}
// 10. SNSリンク
type SocialLinks = {
twitter?: string;
github?: string;
website?: string;
};
function printSocialLinks(links: SocialLinks) {
const twitter = links.twitter ?? "未登録";
const github = links.github ?? "未登録";
const website = links.website ?? "未登録";
console.log("Twitter:", twitter);
console.log("GitHub:", github);
console.log("Website:", website);
}
TypeScriptDay 20のまとめ
Day 20では、
?(Optional)で「あるかもしれない」プロパティを表現する方法null/undefinedを意識して扱う重要性- Optional Chaining
?.で「途中がないかもしれない」オブジェクトを安全にたどる方法 - Nullish Coalescing
??で「本当に値がないときだけ代わりを使う」書き方 - 10パターンの具体例を通して、「存在しないかもしれない」を自然に処理する感覚
を、コードとコメント付きで体験しました。
アプリの世界では、「全部きれいに埋まっているデータ」のほうがむしろ少ないくらいです。 だからこそ、?・?.・?? を味方につけて、「抜けていても落ちない」「抜けていても優しい」コードを書けるようになっていくと、開発体験もユーザー体験もぐっと良くなっていきます。
