はじめてのTypeScriptプログラミング入門 30日コース | TypeScriptの核心「型」 - Day 17:Union Type

30日で身につけるTypeScript
スポンサーリンク
スポンサーリンク

Day 17:Union Typeで「ユーザー権限」を型として表現する

第3週は、TypeScriptの核心である「型」をじっくり育てていく週です。 Day 17のテーマは Union Type と Literal Type。 今日は、これらを使って「ユーザー権限(admin / user / guest)」を型で表現していきます。

Union Typeとは:値の「候補」を型で表す

「AかBかCか」を型で書ける

Union Typeは、ざっくり言うと 「この値はAかBかCのどれかです」と型で宣言する仕組みです。

記号は |(パイプ)を使います。

// 数値か文字列のどちらか
type NumberOrString = number | string;

let value: NumberOrString;

value = 123;      // OK(number)
value = "hello";  // OK(string)
// value = true;  // NG(booleanは許可していない)
TypeScript
  • number | string は、「numberかstringのどちらか」という意味です。
  • Union Typeを使うと、「許可する型の候補」をはっきり書けるようになります。

Literal Typeとは:具体的な「値」を型にする

「この3つの文字列だけOK」を型で表現する

Literal Typeは、具体的な値そのものを型として扱う考え方です。

// "yes" か "no" のどちらかだけを許可する型
type YesNo = "yes" | "no";

let answer: YesNo;

answer = "yes"; // OK
answer = "no";  // OK
// answer = "maybe"; // NG(定義していない文字列)
TypeScript
  • "yes" や "no" のような「文字列リテラル」をそのまま型として使っています。
  • Union Typeと組み合わせることで、「この文字列の候補だけOK」という型を作れます。

この仕組みを使うと、「ユーザー権限は admin / user / guest の3種類だけ」というルールを、型として表現できます。

ユーザー権限を型で表現する:Role型を作る

権限の候補をLiteral Type + Union Typeで定義する

まずは、ユーザー権限を表す Role 型を作ります。

// ユーザー権限を表す型
type Role = "admin" | "user" | "guest";
TypeScript
  • Role は、「admin か user か guest のどれか」という意味の型です。
  • これにより、「権限としてありえる値」を型レベルで制限できます。
// Role型を使った変数
let role1: Role;

role1 = "admin"; // OK
role1 = "user";  // OK
role1 = "guest"; // OK
// role1 = "superuser"; // NG:定義していない権限
TypeScript

重要ポイント(ここはしっかり押さえたいところです):

  • 文字列を自由に書けるようにしてしまうと、"admni" のようなタイプミスも通ってしまいます。
  • Literal Type + Union Typeを使うことで、「この3つ以外は許可しない」というルールを型で表現できます。
  • これは、権限やステータスなど「限られた候補しかない値」を扱うときに、とても強力なパターンです。

Role型をUser型に組み込む

ユーザー情報に権限を持たせる

次に、Role 型を User 型に組み込んでみます。

// ユーザー権限型
type Role = "admin" | "user" | "guest";

// ユーザーを表す型
type User = {
  id: number;      // ユーザーID
  name: string;    // 名前
  email: string;   // メールアドレス
  role: Role;      // 権限(Role型を再利用)
};

// User型を使った例
const adminUser: User = {
  id: 1,
  name: "Admin Taro",
  email: "[email protected]",
  role: "admin",
};

const normalUser: User = {
  id: 2,
  name: "User Hanako",
  email: "[email protected]",
  role: "user",
};

const guestUser: User = {
  id: 3,
  name: "Guest Jiro",
  email: "[email protected]",
  role: "guest",
};
TypeScript

ポイント:

  • role: Role; と書くことで、「ユーザーの権限は必ず admin / user / guest のどれか」というルールになります。
  • もし role: "superadmin" のような値を入れようとすると、コンパイル時にエラーになります。

権限によって動作を変える:Union Typeを使った分岐

Roleに応じてメッセージを変える関数

Union Typeは、「値の候補が限られている」ことがわかっているので、 switch 文などで分岐するときにも、とても相性がよいです。

// 権限に応じてメッセージを返す関数
function getRoleMessage(role: Role): string {
  switch (role) {
    case "admin":
      return "管理者権限です。すべての操作が可能です。";
    case "user":
      return "一般ユーザー権限です。通常の操作が可能です。";
    case "guest":
      return "ゲスト権限です。閲覧のみ可能です。";
    // default は不要(Roleは3つに限定されているため)
  }
}

// テスト
console.log(getRoleMessage("admin"));
console.log(getRoleMessage("user"));
console.log(getRoleMessage("guest"));
TypeScript

深掘りポイント:

  • Role が "admin" | "user" | "guest" に限定されているため、switch のケースも3つだけで済みます。
  • TypeScriptは、「すべての候補を網羅しているか」をチェックしてくれるので、抜け漏れが減ります。
  • もし新しい権限 "superadmin" を追加したら、Role 型と switch の両方を更新することで、コード全体の整合性を保てます。

ユーザー権限チェック関数を作る

adminかどうかを判定する関数

Union Typeを使った権限チェックの例として、「adminかどうか」を判定する関数を作ってみます。

// admin権限かどうかを判定する関数
function isAdmin(user: User): boolean {
  return user.role === "admin";
}

// テスト
console.log(isAdmin(adminUser));   // true
console.log(isAdmin(normalUser));  // false
console.log(isAdmin(guestUser));   // false
TypeScript

guestかどうかを判定する関数

// guest権限かどうかを判定する関数
function isGuest(user: User): boolean {
  return user.role === "guest";
}

console.log(isGuest(adminUser));   // false
console.log(isGuest(guestUser));   // true
TypeScript

ポイント:

  • user.role の型が Role なので、比較対象も "admin" / "user" / "guest" の3つに自然と絞られます。
  • 「ありえない権限」を比較してしまうミスを防げるのが、Literal Type + Union Typeの強みです。

Day 17 練習テンプレート:ユーザー権限を型で表現する

最後に、今日の内容をひとつのファイルにまとめた練習テンプレートを載せます。 このままコピーして、権限の種類を増やしたり、権限ごとの機能制限を追加したりしながら、自分なりの「権限設計」を試してみてください。

// day17-union-role.ts
// Day 17:Union Typeでユーザー権限を型で表現する

// ユーザー権限を表すLiteral + Union Type
type Role = "admin" | "user" | "guest";

// ユーザー型
type User = {
  id: number;
  name: string;
  email: string;
  role: Role;
};

// ユーザー権限に応じたメッセージを返す関数
function getRoleMessage(role: Role): string {
  switch (role) {
    case "admin":
      return "管理者権限です。すべての操作が可能です。";
    case "user":
      return "一般ユーザー権限です。通常の操作が可能です。";
    case "guest":
      return "ゲスト権限です。閲覧のみ可能です。";
  }
}

// admin判定
function isAdmin(user: User): boolean {
  return user.role === "admin";
}

// guest判定
function isGuest(user: User): boolean {
  return user.role === "guest";
}

// テストユーザー
const adminUser: User = {
  id: 1,
  name: "Admin Taro",
  email: "[email protected]",
  role: "admin",
};

const normalUser: User = {
  id: 2,
  name: "User Hanako",
  email: "[email protected]",
  role: "user",
};

const guestUser: User = {
  id: 3,
  name: "Guest Jiro",
  email: "[email protected]",
  role: "guest",
};

// テスト出力
console.log(adminUser.name, ":", getRoleMessage(adminUser.role), "admin?", isAdmin(adminUser));
console.log(normalUser.name, ":", getRoleMessage(normalUser.role), "admin?", isAdmin(normalUser));
console.log(guestUser.name, ":", getRoleMessage(guestUser.role), "guest?", isGuest(guestUser));
TypeScript

Day 17のまとめ

Day 17では、

  • | を使った Union Type で「AかBかCか」という型を表現する方法
  • 具体的な文字列を型にする Literal Type を使って、「権限の候補を限定する」考え方
  • Role = "admin" | "user" | "guest" という型で、ユーザー権限を安全に表現するパターン
  • User 型に role: Role を組み込み、権限に応じてメッセージや動作を変える関数を作る流れ

を、具体的なコードとともに体験しました。

Union TypeとLiteral Typeは、「ありえる値の範囲」を型でしっかり絞り込むための強力な道具です。 ぜひ、ステータス("draft" | "published" | "archived")や支払い方法("card" | "bank" | "cash")など、身近な例でも試してみてください。 型で「世界のルール」を表現できるようになると、コードの安全性と読みやすさがぐっと上がっていきます。


Day 17:Union Typeで「世界のルール」を型にしてみる

第3週は、TypeScriptの核心である「型」をじっくり育てていく週です。 Day 17の演習テーマは、Union Type と Literal Type を使って、よくある「分類・ステータス」を型で表現することです。

今回Union Typeにするのは次の5つです。

  • Role
  • Status
  • Gender
  • PaymentMethod
  • Priority

ひとつひとつ、初心者の方にもイメージしやすいように、ステップバイステップで作っていきます。

Union TypeとLiteral Typeの基本をおさらいする

Union Typeとは

Union Typeは、ざっくり言うと 「この値はAかBかCのどれかです」と型で宣言する仕組みです。 記号は |(パイプ)を使います。

// 数値か文字列のどちらか
type NumberOrString = number | string;

let value: NumberOrString;

value = 123;      // OK(number)
value = "hello";  // OK(string)
// value = true;  // NG(booleanは許可していない)
TypeScript
  • number | string は、「numberかstringのどちらか」という意味です。
  • Union Typeを使うと、「許可する型の候補」をはっきり書けるようになります。

Literal Typeとは

Literal Typeは、具体的な値そのものを型として扱う考え方です。

// "yes" か "no" のどちらかだけを許可する型
type YesNo = "yes" | "no";

let answer: YesNo;

answer = "yes"; // OK
answer = "no";  // OK
// answer = "maybe"; // NG(定義していない文字列)
TypeScript
  • "yes" や "no" のような「文字列リテラル」をそのまま型として使っています。
  • Union Typeと組み合わせることで、「この文字列の候補だけOK」という型を作れます。

この仕組みを使って、現実世界の「分類・ステータス」を型にしていきます。

RoleをUnion Typeにする(ユーザー権限)

Roleの設計

まずは、ユーザー権限を表す Role から始めます。 よくある権限として、次の3つを想定します。

  • admin
  • user
  • guest
// ユーザー権限を表す型
type Role = "admin" | "user" | "guest";

// Role型を使った例
let role: Role;

role = "admin"; // OK
role = "user";  // OK
role = "guest"; // OK
// role = "superuser"; // NG:定義していない権限
TypeScript

重要ポイント:

  • 文字列を自由に書けるようにしてしまうと、"admni" のようなタイプミスも通ってしまいます。
  • Literal Type + Union Typeを使うことで、「この3つ以外は許可しない」というルールを型で表現できます。
  • 権限やステータスなど「限られた候補しかない値」を扱うときに、とても強力なパターンです。

StatusをUnion Typeにする(状態・ステータス)

Statusの設計

次に、よくある「状態」を表す Status を作ります。 例えば、次の3つを想定します。

  • active(有効)
  • inactive(無効)
  • pending(保留)
// ステータスを表す型
type Status = "active" | "inactive" | "pending";

let status: Status;

status = "active";   // OK
status = "inactive"; // OK
status = "pending";  // OK
// status = "deleted"; // NG:定義していないステータス
TypeScript

ポイント:

  • StatusをUnion Typeにしておくと、「このオブジェクトは必ずこの3つのどれかの状態」という前提でコードを書けます。
  • 例えば、画面表示やボタンの有効・無効をStatusに応じて切り替えるときに便利です。

GenderをUnion Typeにする(性別の表現)

Genderの設計

性別の表現は、システムによってさまざまですが、ここではシンプルに次の3つを想定します。

  • male
  • female
  • other
// 性別を表す型
type Gender = "male" | "female" | "other";

let gender: Gender;

gender = "male";   // OK
gender = "female"; // OK
gender = "other";  // OK
// gender = "unknown"; // NG:定義していない値
TypeScript

大事なポイント:

  • Genderのようなセンシティブな情報は、現実の要件に合わせて慎重に設計する必要があります。
  • ここでは学習用としてシンプルな3種類にしていますが、実際のサービスでは「未回答」や「自由入力」などの選択肢も検討されます。
  • Union Typeは、「システムとしてどういう選択肢を用意するか」を型で明確にするための道具だと考えてください。

PaymentMethodをUnion Typeにする(支払い方法)

PaymentMethodの設計

次に、支払い方法を表す PaymentMethod を作ります。 よくある支払い方法として、次の4つを想定します。

  • card(クレジットカード)
  • bank(銀行振込)
  • cash(現金)
  • wallet(電子ウォレットなど)
// 支払い方法を表す型
type PaymentMethod = "card" | "bank" | "cash" | "wallet";

let payment: PaymentMethod;

payment = "card";   // OK
payment = "bank";   // OK
payment = "cash";   // OK
payment = "wallet"; // OK
// payment = "crypto"; // NG:定義していない支払い方法
TypeScript

ポイント:

  • PaymentMethodをUnion Typeにしておくと、「このサービスはどの支払い方法を受け付けるか」が型として明確になります。
  • 新しい支払い方法を追加するときは、PaymentMethod 型に候補を足すことで、関連するコードの見直しもしやすくなります。

PriorityをUnion Typeにする(優先度)

Priorityの設計

最後に、タスクやチケットなどでよく使う「優先度」を表す Priority を作ります。 ここでは、次の3つを想定します。

  • low
  • medium
  • high
// 優先度を表す型
type Priority = "low" | "medium" | "high";

let priority: Priority;

priority = "low";    // OK
priority = "medium"; // OK
priority = "high";   // OK
// priority = "urgent"; // NG:定義していない優先度
TypeScript

深掘りポイント:

  • PriorityをUnion Typeにしておくと、「優先度に応じて表示色を変える」「通知の強さを変える」といった処理が書きやすくなります。
  • 例えば、switch 文で分岐するときも、3つのケースだけを考えればよくなり、抜け漏れが減ります。

5つのUnion Typeをまとめて使ってみる

練習用のUser型を作る

5つのUnion Typeを組み合わせて、練習用の User 型を作ってみます。

// 5つのUnion Type定義
type Role = "admin" | "user" | "guest";
type Status = "active" | "inactive" | "pending";
type Gender = "male" | "female" | "other";
type PaymentMethod = "card" | "bank" | "cash" | "wallet";
type Priority = "low" | "medium" | "high";

// ユーザー型(練習用)
type User = {
  id: number;
  name: string;
  role: Role;
  status: Status;
  gender: Gender;
  favoritePayment: PaymentMethod;
  supportPriority: Priority;
};

// User型を使った例
const user1: User = {
  id: 1,
  name: "Taro",
  role: "admin",
  status: "active",
  gender: "male",
  favoritePayment: "card",
  supportPriority: "high",
};

const user2: User = {
  id: 2,
  name: "Hanako",
  role: "user",
  status: "pending",
  gender: "female",
  favoritePayment: "cash",
  supportPriority: "medium",
};
TypeScript

Priorityに応じてメッセージを変える関数

// 優先度に応じてメッセージを返す関数
function getPriorityMessage(priority: Priority): string {
  switch (priority) {
    case "low":
      return "優先度は低めです。時間があるときに対応してください。";
    case "medium":
      return "通常の優先度です。なるべく早めに対応してください。";
    case "high":
      return "高い優先度です。できるだけすぐに対応してください。";
  }
}

// テスト
console.log(user1.name, "のサポート優先度:", getPriorityMessage(user1.supportPriority));
console.log(user2.name, "のサポート優先度:", getPriorityMessage(user2.supportPriority));
TypeScript

Day 17 演習テンプレート:5種類のUnion Type

最後に、今日の内容をひとつのファイルにまとめた練習テンプレートを載せます。 このままコピーして、候補を増やしたり、関数を追加したりしながら、自分なりの「世界のルール」を型で表現してみてください。

// day17-union-practice.ts
// Day 17:5種類のUnion Type 演習テンプレート

// 1. Role(権限)
type Role = "admin" | "user" | "guest";

// 2. Status(状態)
type Status = "active" | "inactive" | "pending";

// 3. Gender(性別)
type Gender = "male" | "female" | "other";

// 4. PaymentMethod(支払い方法)
type PaymentMethod = "card" | "bank" | "cash" | "wallet";

// 5. Priority(優先度)
type Priority = "low" | "medium" | "high";

// 練習用User型
type User = {
  id: number;
  name: string;
  role: Role;
  status: Status;
  gender: Gender;
  favoritePayment: PaymentMethod;
  supportPriority: Priority;
};

// 優先度メッセージ関数
function getPriorityMessage(priority: Priority): string {
  switch (priority) {
    case "low":
      return "優先度は低めです。時間があるときに対応してください。";
    case "medium":
      return "通常の優先度です。なるべく早めに対応してください。";
    case "high":
      return "高い優先度です。できるだけすぐに対応してください。";
  }
}

// テストユーザー
const user1: User = {
  id: 1,
  name: "Taro",
  role: "admin",
  status: "active",
  gender: "male",
  favoritePayment: "card",
  supportPriority: "high",
};

const user2: User = {
  id: 2,
  name: "Hanako",
  role: "user",
  status: "pending",
  gender: "female",
  favoritePayment: "cash",
  supportPriority: "medium",
};

console.log(user1.name, "のサポート優先度:", getPriorityMessage(user1.supportPriority));
console.log(user2.name, "のサポート優先度:", getPriorityMessage(user2.supportPriority));
TypeScript

Day 17のまとめ

Day 17では、

  • | を使った Union Type で「AかBかCか」という型を表現する方法
  • 具体的な文字列を型にする Literal Type を使って、「候補を限定する」考え方
  • Role / Status / Gender / PaymentMethod / Priority の5つをUnion Typeとして設計する流れ
  • それらを組み合わせて User 型を作り、優先度に応じてメッセージを変える関数を書く練習

を、具体的なコードとともに体験しました。

Union TypeとLiteral Typeは、「ありえる値の範囲」を型でしっかり絞り込むための強力な道具です。 ぜひ、ステータスや権限、支払い方法など、身近な例でもどんどん試してみてください。 型で「世界のルール」を表現できるようになると、コードの安全性と読みやすさがぐっと上がっていきます。

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