第8章 型の武器①:リテラル型・ユニオン型で選択肢を固定🎫✨

第7章で「string とか number だけだと、意味が混ざって事故る〜!😱」を体験したよね。
この章は、その“事故りやすい自由さ”を 「そもそも選べる値を固定する」 ことで止める話だよ🛡️💕
ちなみに本日時点のTypeScriptは 5.9 系のリリースノートが最新として参照できる状態で(2026年1月更新)、今日やる型テクはぜんぶ現役バリバリで使えるよ〜😊✨ (typescriptlang.org)
この章でできるようになること🎯🌸
- 「この値は この中からしか選べない」を型で表現できる🎫✨
switchの分岐漏れを コンパイルで怒らせる 感覚がわかる⚡as constを使って「定義した選択肢」から型を自動生成できる🤖💡satisfiesで「辞書(マップ)のキー漏れ・スペルミス」を叩ける🔨✨ (typescriptlang.org)
まずは“事故”を見てみよっか😇➡️💥
「プラン」は Free / Pro しかないのに、string だと何でも入るよね。
type User = {
plan: string; // 😇 なんでも入っちゃう
};
const u: User = { plan: "Fre" }; // typoでも通る…🥲
この時点で 不変条件(プランは決められた候補のどれか)を破ってるのに、型が止めてくれないのがつらいところ😵💫
1) リテラル型ってなに?🧠✨
TypeScriptには「文字列そのもの」を型にできる仕組みがあるよ🙂
たとえば "Free" は string の一種だけど、もっと具体的に "Free そのもの" を指す型なんだ〜! (typescriptlang.org)
- 文字列リテラル型:
"Free" - 数値リテラル型:
0,1,100 - 真偽値リテラル型:
true,false(typescriptlang.org)
2) ユニオン型で「選択肢の集合」を作る🎫✨
じゃあ「プランは Free か Pro」って型にしちゃおう!
type Plan = "Free" | "Pro";
type User = {
plan: Plan;
};
const ok: User = { plan: "Free" }; // ✅
const ng: User = { plan: "Fre" }; // ❌ コンパイルで怒られる⚡
**「無効な値が入るルート」**が一気に減るよ〜〜〜🛡️✨
3) どんな場面に効く?(めちゃ効くやつ)💪😊
ユニオン型で固定しやすいのはこういうの👇✨
- ステータス:
"Draft" | "Paid" | "Shipped"🚚 - 種類(Kind):
"Personal" | "Business"🧾 - 区分(Category):
"Food" | "Book" | "Other"🛍️ - UIのタブ:
"Home" | "Settings"🧭 - エラーコード(軽め):
"NotFound" | "Unauthorized"🚫
4) “定義した配列”から型を作る(Single Source of Truth)📌✨
ここからが超大事! 値の一覧(配列)と型定義を別々に書くと、更新漏れが起きがち🥲
そこでよく使うのが as const ✨
as const は「できるだけリテラルとして扱ってね」ってお願いするやつだよ🙂
(const assertion と呼ばれてて、TypeScript公式の機能として説明されてるよ) (typescriptlang.org)
const PLANS = ["Free", "Pro"] as const;
// ^^^^^ readonly ["Free", "Pro"] みたいに超カチカチになる✨
type Plan = typeof PLANS[number];
// ^^^^^^^^^^^^^^^^^^^ "Free" | "Pro"
これで…
- 値の追加は
PLANSに足すだけ✅ - 型
Planも自動で増える✅
最高〜〜〜😆✨
5) 境界から来る入力を「Planにする」🕵️♀️➡️💎
UIやAPIから来る入力は、だいたい string とか unknown だよね。
ユニオン型は 実行時には存在しないから、ここは 実行時チェックが必要🙂
シンプル版いくよ👇✨
const PLANS = ["Free", "Pro"] as const;
type Plan = typeof PLANS[number];
function parsePlan(input: unknown): Plan | null {
if (typeof input !== "string") return null;
// includes の型都合を軽く調整🙂
const ok = (PLANS as readonly string[]).includes(input);
return ok ? (input as Plan) : null;
}
使う側はこう👇
const raw = "Pro"; // ほんとは外部入力想定
const plan = parsePlan(raw);
if (plan === null) {
console.log("プランが不正です🥲");
} else {
// plan は Plan 型("Free" | "Pro")になってる💎
console.log("OK:", plan);
}
この「境界でチェックして、ドメイン内は信じる」方針は、ロードマップ後半(境界・DTO・バリデーション)で超効いてくるよ〜🛡️✨
6) switch の分岐漏れを “型で” 潰す🚦⚡
Planが増えたときに、分岐を書き忘れるのも事故ポイント😱 ここも型でガードできるよ!
type Plan = "Free" | "Pro";
function assertNever(x: never): never {
throw new Error("Unexpected value: " + x);
}
function planLabel(plan: Plan): string {
switch (plan) {
case "Free":
return "無料プラン🆓";
case "Pro":
return "プロプラン⭐";
default:
return assertNever(plan); // ここが効く⚡
}
}
Plan に "Enterprise" を足したら、コンパイルが「switch足りないよ!」って教えてくれる感じになるよ🎉
(この“絞り込み(narrowing)”の考え方は公式ハンドブックでも詳しく説明されてるよ) (typescriptlang.org)
7) satisfies で「辞書のキー」を安全にする🗂️✨
「プランごとの価格表」みたいな辞書、スペルミスしがちじゃない?🥺
satisfies は 型を満たしてるか検証しつつ、推論はなるべく保つ 便利オペレータだよ✨(TypeScript 4.9で入ったよ) (typescriptlang.org)
const PLANS = ["Free", "Pro"] as const;
type Plan = typeof PLANS[number];
const PRICE_BY_PLAN = {
Free: 0,
Pro: 980,
// Por: 980, // ←こういうミスを即発見できる💥
} satisfies Record<Plan, number>;
これ、地味だけど 運用でめちゃ効く やつ〜〜〜🥰✨
8) enum とユニオン、どっち使う?🤔💡
サクッと結論だけ言うね🙂
-
ユニオン型(おすすめ)
- 型だけで完結(実行時の余計なものが増えない)
- “候補が少ない” “ドメインの選択肢” に強い🎫✨
-
enum
- 実行時にもオブジェクトとして残る
- 外部との取り決め(SDK/設定値)で「実体がほしい」時に便利なこともある🙂
- enum自体も「各メンバーがリテラルになって、全体がユニオンっぽく扱える」流れがあるよ (typescriptlang.org)
この教材ではまず ユニオン型をメイン武器にするのが気持ちいいよ🛡️✨
ミニ課題(手を動かすやつ)🧪🌟
課題A:固定できる選択肢を3つ探す🔍✨
あなたの題材から、次のどれかを3つ選んで「候補を列挙」してね🙂 (例:ステータス、種別、カテゴリ、画面タブ、支払い方法…)
課題B:as const 配列 → ユニオン型を作る🎫✨
const XXX = [...] as consttype Xxx = typeof XXX[number]
課題C:satisfies で辞書を作る🗂️✨
Record<Xxx, ...>を使って「キー漏れゼロ」を体験してね😊
課題D:境界の parseXxx() を1個作る🚪✅
unknownを受けて- OKなら型付きで返す / ダメなら
null
AI活用テンプレ(この章向け)🤖💬✨
コピペで使ってOK〜〜😆🌸
- 「この機能で“固定できる選択肢”を10個候補出して。ステータス、カテゴリ、種別など」🔍
- 「
as const配列からユニオン型を作るパターンを、私の題材向けに例で出して」🧩 - 「
satisfies Record<...>で辞書のキー漏れを防ぐ例を作って」🗂️ - 「switchの分岐漏れをコンパイルで検知する
assertNeverパターンを、初心者向けに説明して」🚦
まとめ💎✨
- リテラル型で「この値そのもの」を型にできる🙂 (typescriptlang.org)
- ユニオン型で「選択肢の集合」を固定できる🎫✨
- **
as const+typeof XXX[number]**で、値リストから型を自動生成できる🪄 (typescriptlang.org) - **
satisfies**で辞書のスペルミス・キー漏れを潰せる🔨✨ (typescriptlang.org)
次の第9章は、このユニオン型をさらに強くする **「タグ付きユニオン(Discriminated Union)」**で、状態や結果(Success/Failure)を安全に表現していくよ〜🏷️🧠✨