はじめてのTypeScriptプログラミング入門 30日コース | TypeScriptの核心「型」 - Day 20:Optional・null・undefined

30日で身につけるTypeScript
スポンサーリンク
スポンサーリンク
  1. Day 20:Optional・null・undefinedで「ないかもしれない」を安全に扱う
  2. Optional・null・undefinedの基本を整理する
    1. null と undefined のざっくりイメージ
    2. Optional(?)とは:あってもなくてもいいプロパティ
  3. Optional Chaining ?.:安全に「あるかもしれないもの」をたどる
    1. まずは素直なアクセスを書いてみる
    2. ?. を使って「あるときだけ」安全にアクセスする
  4. Nullish Coalescing ??:なかったときの「代わり」を用意する
    1. ?? の基本イメージ
  5. 実践:プロフィール情報を安全に表示する
    1. サンプルコードを分解して理解する
    2. コメント付きで書き直してみる
  6. もう少し現実的なプロフィール例を作ってみる
    1. Optionalなプロパティを持つ型を定義する
    2. 安全にプロフィールを表示する関数
  7. Day 20 練習テンプレート:Optional・?.・?? をまとめて体験する
  8. Day 20のまとめ
  9. Day 20:Optional・null・undefinedで「存在しないかもしれない」を10パターンで体験する
  10. 基本の考え方を軽くおさらいする
    1. Optional(?)とは
    2. Optional Chaining ?. とは
    3. Nullish Coalescing ?? とは
  11. 10パターンで「存在しないかもしれない」を処理してみる
    1. 1. ニックネーム(プロフィールの基本)
    2. 2. 自己紹介文(bio)が未設定かもしれない
    3. 3. 年齢が未入力かもしれない
    4. 4. 住所情報が部分的に欠けているかもしれない
    5. 5. 電話番号リストが存在しないかもしれない
    6. 6. 設定値(テーマ)が未設定かもしれない
    7. 7. 最終ログイン日時がないかもしれない
    8. 8. カートの割引情報がないかもしれない
    9. 9. 会社情報が未登録かもしれない
    10. 10. SNSリンクが一部だけ存在するかもしれない
  12. Day 20 練習テンプレート:10パターンをまとめて試せるコード
  13. Day 20のまとめ

Day 20:Optional・null・undefinedで「ないかもしれない」を安全に扱う

第3週は、TypeScriptの核心である「型」をじっくり育てていく週です。 Day 20のテーマは 「存在しないかもしれない値」をどう安全に扱うか です。

キーワードは次の5つです。

  • ?(Optional)
  • null
  • undefined
  • 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なし)
TypeScript
  • nickname?: 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);
TypeScript
  • user.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

これをステップバイステップで見ていきます。

  1. user.profile?.nickname
    • profile が存在するなら nickname を読む
    • profile がないなら undefined になる
  2. ... ?? "未設定"
    • 左側が 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);
TypeScript

Day 20のまとめ

Day 20では、

  • null / undefined のざっくりした違い
  • ?(Optional)で「あるかもしれない」プロパティを表現する方法
  • Optional Chaining ?. で「途中がないかもしれない」オブジェクトを安全にたどる方法
  • Nullish Coalescing ?? で「本当に値がないときだけ代わりを使う」書き方
  • プロフィール情報を題材に、「安全で優しい表示」を実現するコード

を、具体的な例とともに体験しました。

「ないかもしれない」をちゃんと扱えるようになると、 エラーで落ちないだけでなく、ユーザーにとっても気持ちのよい挙動を作れるようになります。 ぜひ、設定画面やプロフィール画面など、身近な場面を思い浮かべながら、 ?・?.・?? を自分のものにしていってください。


Day 20:Optional・null・undefinedで「存在しないかもしれない」を10パターンで体験する

第3週は、TypeScriptの核心である「型」をじっくり育てていく週です。 Day 20の演習テーマは、「存在しない可能性があるデータ」を10パターン処理してみることです。

キーワードは次の5つです。

  • ?(Optional)
  • null
  • undefined
  • Optional Chaining ?.
  • Nullish Coalescing ??

ここでは、よくあるアプリの場面をイメージしながら、 「あるかもしれない」「ないかもしれない」を安全に、そして読みやすく扱う書き方を身につけていきます。

基本の考え方を軽くおさらいする

Optional(?)とは

? は、「このプロパティはあってもなくてもいいですよ」という意味です。

type Profile = {
  nickname?: string; // あってもなくてもよい
};
TypeScript
  • nickname?: string は、「stringかもしれないし、そもそもプロパティがないかもしれない」という型です。

Optional Chaining ?. とは

?. は、「途中がないかもしれないけど、あったら先に進む」ための安全なアクセス方法です。

user.profile?.nickname; // profileがなければundefinedになる
TypeScript

Nullish Coalescing ?? とは

?? は、「左側がnullかundefinedのときだけ、右側の値を使う」演算子です。

const nickname = user.profile?.nickname ?? "未設定";
TypeScript
  • null / 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);
}
TypeScript

Day 20のまとめ

Day 20では、

  • ?(Optional)で「あるかもしれない」プロパティを表現する方法
  • null / undefined を意識して扱う重要性
  • Optional Chaining ?. で「途中がないかもしれない」オブジェクトを安全にたどる方法
  • Nullish Coalescing ?? で「本当に値がないときだけ代わりを使う」書き方
  • 10パターンの具体例を通して、「存在しないかもしれない」を自然に処理する感覚

を、コードとコメント付きで体験しました。

アプリの世界では、「全部きれいに埋まっているデータ」のほうがむしろ少ないくらいです。 だからこそ、?・?.・?? を味方につけて、「抜けていても落ちない」「抜けていても優しい」コードを書けるようになっていくと、開発体験もユーザー体験もぐっと良くなっていきます。

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