はじめてのTypeScriptプログラミング入門 30日コース | Generics・Utility Types・Class・Module - Day 24:Utility Types

TypeScript 30日で身につけるTypeScript
スポンサーリンク
スポンサーリンク
  1. Day 24:Utility Typesで「Userまわりの型設計」を一気に楽にする
  2. User型をベースとして用意する
  3. Utility Typesのざっくりイメージ
    1. Partial
    2. Required
    3. Readonly
    4. Pick
    5. Omit
  4. Step 1:CreateUser型を作る
    1. 「作成時に必要な情報」を考える
    2. OmitとRequiredを組み合わせる
  5. Step 2:UpdateUser型を作る
    1. 「更新時に必要な情報」を考える
    2. PartialとPickを組み合わせる
  6. Step 3:UserSummary型を作る
    1. 「一覧表示用の軽い情報」を考える
    2. Pickで必要な項目だけを取り出す
  7. Step 4:Readonlyを使って「変更禁止」を表現する
    1. 「読み取り専用のUser」を考える
  8. Step 5:User / CreateUser / UpdateUser / UserSummary をまとめて使ってみる
  9. Day 24のまとめ
  10. Day 24:Utility Types演習「既存型から5種類以上の新しい型を作る」
  11. ベースとなる既存型を決める:User型
  12. Step 1:Partialで「一部だけ更新したいUser」を作る
    1. Partialのイメージ
    2. UpdateUserPartial型を作る
  13. Step 2:Requiredで「すべて必須のUser」を作る
    1. Requiredのイメージ
    2. StrictUser型を作る
  14. Step 3:Readonlyで「変更禁止のUser」を作る
    1. Readonlyのイメージ
    2. ReadonlyUser型を作る
  15. Step 4:Pickで「表示用の軽いUser」を作る
    1. Pickのイメージ
    2. UserSummary型を作る
  16. Step 5:Omitで「作成用のUser」を作る
    1. Omitのイメージ
    2. CreateUser型を作る
  17. Step 6:Utility Typesを組み合わせて、さらに派生型を増やす
    1. UpdateUser型(id必須+その他任意)
  18. Step 7:作った型を実際に使ってみる
  19. Day 24演習のまとめ

Day 24:Utility Typesで「Userまわりの型設計」を一気に楽にする

第4週のDay 24では、TypeScriptの便利機能である Utility Types を使って、 ひとつの User 型から、いくつもの派生型をスマートに作っていきます。

扱うUtility Typesは次の5つです。

  • Partial
  • Required
  • Readonly
  • Pick
  • Omit

そして、実践テーマはこうです。

User型から

  • User
  • CreateUser
  • UpdateUser
  • UserSummary を作る。

「同じような型を何度も手書きする」のではなく、 「元の型をうまく再利用する」 という発想を身につけていきます。

User型をベースとして用意する

まずは、ベースとなる User 型を決めます。 ここでは、シンプルなユーザー情報を想定します。

// ベースとなるUser型
type User = {
  id: number;          // ユーザーID(DBで採番されるイメージ)
  name: string;        // 名前
  email: string;       // メールアドレス
  age?: number;        // 年齢(任意)
  isActive: boolean;   // 有効ユーザーかどうか
};
TypeScript

この User をもとに、 「作成用」「更新用」「表示用」などの派生型をUtility Typesで作っていきます。

Utility Typesのざっくりイメージ

Partial

Partial<T> は、

「Tのすべてのプロパティを任意(オプショナル)にした型」

です。

type PartialUser = Partial<User>;
TypeScript

PartialUser は、User の各プロパティがすべて ? 付きになったイメージです。

Required

Required<T> は、

「Tのすべてのプロパティを必須にした型」

です。

type RequiredUser = Required<User>;
TypeScript

User で age? のように任意だったものも、 RequiredUser では必須になります。

Readonly

Readonly<T> は、

「Tのすべてのプロパティを読み取り専用にした型」

です。

type ReadonlyUser = Readonly<User>;
TypeScript

ReadonlyUser のプロパティは、代入で変更できなくなります。

Pick

Pick<T, K> は、

「Tから指定したプロパティだけを取り出した型」

です。

type UserNameAndEmail = Pick<User, "name" | "email">;
TypeScript

User のうち、name と email だけを持つ型になります。

Omit

Omit<T, K> は、

「Tから指定したプロパティを除いた型」

です。

type UserWithoutEmail = Omit<User, "email">;
TypeScript

User から email を取り除いた型になります。

Step 1:CreateUser型を作る

「作成時に必要な情報」を考える

ユーザー作成時には、通常 id はまだありません(DB側で採番されることが多いです)。 そのため、CreateUser では、

  • id は不要
  • name / email / isActive は必須
  • age は任意でもよい

というイメージで設計してみます。

OmitとRequiredを組み合わせる

// Userからidを除いた型
type UserWithoutId = Omit<User, "id">;

// 作成用のUser(すべて必須にしたい場合)
type CreateUser = Required<UserWithoutId>;
TypeScript

ここでの流れはこうです。

  1. Omit<User, "id"> で「idを除いたUser」を作る
  2. Required<...> で「残りのプロパティをすべて必須」にする

これにより、CreateUser は次のようなイメージになります。

// 展開イメージ(実際には自動で生成されます)
type CreateUser = {
  name: string;
  email: string;
  age: number;       // 必須に変わる
  isActive: boolean;
};
TypeScript

ポイント:

  • 「作成時にはidはいらない」というビジネスルールを、型に落とし込めています。
  • Required を使うことで、「作成時には全部ちゃんと渡してね」という制約を型で表現できます。

Step 2:UpdateUser型を作る

「更新時に必要な情報」を考える

更新時には、

  • id は必須(どのユーザーを更新するかを特定するため)
  • それ以外の項目は「変更したいものだけ渡せばよい」

というケースが多いです。

つまり、

「idは必須、それ以外は任意」

という型を作りたいわけです。

PartialとPickを組み合わせる

// idだけを必須にしたいので、まずはidだけの型を作る
type UserIdOnly = Pick<User, "id">;

// id以外のプロパティを取り出す
type UserWithoutIdForUpdate = Omit<User, "id">;

// 更新用のUser型
type UpdateUser = UserIdOnly & Partial<UserWithoutIdForUpdate>;
TypeScript

ここでの流れはこうです。

  1. Pick<User, "id"> で「idだけを持つ型」を作る
  2. Omit<User, "id"> で「id以外のプロパティ」を取り出す
  3. Partial<...> で「id以外のプロパティをすべて任意」にする
  4. &(Intersection)で「id必須 + その他任意」を合成する

UpdateUser のイメージは次のようになります。

// 展開イメージ
type UpdateUser = {
  id: number;        // 必須
  name?: string;     // 任意
  email?: string;    // 任意
  age?: number;      // 任意
  isActive?: boolean;// 任意
};
TypeScript

重要ポイント:

  • Partial は「更新用の型」と相性がとても良いです。
  • 「どの項目を更新するかは呼び出し側に任せる」という柔らかい設計ができます。
  • それでも id は必須なので、「どのユーザーか分からない更新」はコンパイル時に防げます。

Step 3:UserSummary型を作る

「一覧表示用の軽い情報」を考える

ユーザー一覧画面などでは、 すべての詳細情報を表示する必要はなく、 たとえば次のような項目だけで十分なことが多いです。

  • id
  • name
  • email

ここでは、「一覧表示用の軽い型」として UserSummary を作ってみます。

Pickで必要な項目だけを取り出す

// 一覧表示用のUserSummary
type UserSummary = Pick<User, "id" | "name" | "email">;
TypeScript

これだけで、

// 展開イメージ
type UserSummary = {
  id: number;
  name: string;
  email: string;
};
TypeScript

という型が自動的に生成されます。

ポイント:

  • Pick は「表示用」「レスポンス用」など、 特定の場面で使う軽量な型を作るときにとても便利です。
  • 元の User 型を変更せずに、 「この場面ではこれだけあればいい」という型を簡単に作れます。

Step 4:Readonlyを使って「変更禁止」を表現する

「読み取り専用のUser」を考える

たとえば、「ログ出力用」や「履歴表示用」など、 「絶対に変更してほしくないUser情報」を扱う場面もあります。

そのときに使えるのが Readonly<User> です。

// 読み取り専用のUser
type ReadonlyUser = Readonly<User>;
TypeScript

ReadonlyUser のプロパティは、代入で変更できなくなります。

const u: ReadonlyUser = {
  id: 1,
  name: "Taro",
  email: "[email protected]",
  isActive: true,
};

// u.name = "Hanako"; // コンパイルエラー:Readonlyなので変更不可
TypeScript

ポイント:

  • Readonly は「このオブジェクトは変更してはいけない」という意図を、 型としてはっきり表現できます。
  • セキュリティやバグ防止の観点でも、とても役に立つUtility Typeです。

Step 5:User / CreateUser / UpdateUser / UserSummary をまとめて使ってみる

最後に、ここまで作った型を使って、 簡単な関数をいくつか書いてみます。

// ベースUser型
type User = {
  id: number;
  name: string;
  email: string;
  age?: number;
  isActive: boolean;
};

// CreateUser
type UserWithoutId = Omit<User, "id">;
type CreateUser = Required<UserWithoutId>;

// UpdateUser
type UserIdOnly = Pick<User, "id">;
type UserWithoutIdForUpdate = Omit<User, "id">;
type UpdateUser = UserIdOnly & Partial<UserWithoutIdForUpdate>;

// UserSummary
type UserSummary = Pick<User, "id" | "name" | "email">;

// ReadonlyUser
type ReadonlyUser = Readonly<User>;

// ===== 使用例 =====

// ユーザー作成関数
function createUser(data: CreateUser): User {
  // 本来はDBなどでidを採番する
  const newUser: User = {
    id: Math.floor(Math.random() * 10000),
    ...data,
  };
  console.log("ユーザーを作成しました:", newUser);
  return newUser;
}

// ユーザー更新関数
function updateUser(user: User, update: UpdateUser): User {
  if (user.id !== update.id) {
    throw new Error("IDが一致しません");
  }

  // updateの任意項目を上書きする
  const updated: User = {
    ...user,
    ...update,
  };

  console.log("ユーザーを更新しました:", updated);
  return updated;
}

// UserSummaryを返す関数
function toUserSummary(user: User): UserSummary {
  return {
    id: user.id,
    name: user.name,
    email: user.email,
  };
}

// ReadonlyUserを使う例
function logUser(user: ReadonlyUser): void {
  console.log("ユーザー情報(読み取り専用):", user);
}

// ===== 実際に使ってみる =====

const created = createUser({
  name: "Taro",
  email: "[email protected]",
  age: 20,
  isActive: true,
});

const updated = updateUser(created, {
  id: created.id,
  email: "[email protected]",
});

const summary = toUserSummary(updated);
console.log("UserSummary:", summary);

logUser(updated);
// updated.name = "Hanako"; // ReadonlyUserではないのでここはOKだが、ReadonlyUserならエラーになります
TypeScript

Day 24のまとめ

Day 24では、

  • Partial / Required / Readonly / Pick / Omit というUtility Typesの役割
  • ベースとなる User 型から、
    • 作成用の CreateUser
    • 更新用の UpdateUser
    • 一覧表示用の UserSummary を組み立てる流れ
  • 「ビジネスルール(作成時はid不要、更新時はid必須など)」を、 型として表現する考え方

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

Utility Typesは、「同じような型を何度も手書きする」苦労を減らしてくれるだけでなく、 「意図を型に刻み込む」ための強力な道具でもあります。

今日の User 周りの設計をベースに、 ぜひ Product や Order など、他のドメインでも 「Create〜」「Update〜」「〜Summary」をUtility Typesで作ってみてください。 そうしていくうちに、型設計そのものがどんどん楽しくなっていくはずです。


Day 24:Utility Types演習「既存型から5種類以上の新しい型を作る」

Day 24の演習テーマは、とても実務寄りでおもしろい内容です。

ひとつの既存型から、 Utility Typesを使って5種類以上の新しい型を作る。

ここまで学んできた

  • Partial
  • Required
  • Readonly
  • Pick
  • Omit

を総動員して、「型の再利用力」を一気に高めていきます。

今回は、ひとつのベース型として User を用意し、そこから派生型をどんどん作っていく流れで説明していきます。

ベースとなる既存型を決める:User型

まずは、演習の土台となる既存型をひとつ決めます。 ここでは、シンプルなユーザー情報を表す User 型を使います。

// 既存のUser型(ベースになる型)
type User = {
  id: number;          // ユーザーID
  name: string;        // 名前
  email: string;       // メールアドレス
  age?: number;        // 年齢(任意)
  isActive: boolean;   // 有効ユーザーかどうか
};
TypeScript

この User から、Utility Typesを使って 少なくとも5種類以上の新しい型を作っていきます。

Step 1:Partialで「一部だけ更新したいUser」を作る

Partialのイメージ

Partial<T> は、

「Tのすべてのプロパティを任意(オプショナル)にした型」

です。

更新処理などで、「変更したい項目だけ渡したい」という場面にぴったりです。

UpdateUserPartial型を作る

// Userのすべてのプロパティを任意にした型
type UpdateUserPartial = Partial<User>;
TypeScript

UpdateUserPartial のイメージは次のようになります。

// 展開イメージ(実際には自動で生成されます)
type UpdateUserPartial = {
  id?: number;
  name?: string;
  email?: string;
  age?: number;
  isActive?: boolean;
};
TypeScript

ポイント:

  • 「どの項目を更新するかは呼び出し側に任せたい」ときに便利です。
  • ただし、id まで任意になってしまうので、 実際の更新用型では「idだけ必須」にするなどの工夫を組み合わせることが多いです。

Step 2:Requiredで「すべて必須のUser」を作る

Requiredのイメージ

Required<T> は、

「Tのすべてのプロパティを必須にした型」

です。

User では age? のように任意だったものも、 Required<User> では必須になります。

StrictUser型を作る

// すべてのプロパティが必須のUser
type StrictUser = Required<User>;
TypeScript

StrictUser のイメージは次のようになります。

// 展開イメージ
type StrictUser = {
  id: number;
  name: string;
  email: string;
  age: number;        // 必須に変わる
  isActive: boolean;
};
TypeScript

ポイント:

  • 「この場面では、全部の情報が揃っているUserしか扱わない」という きっちりした前提を型で表現できます。
  • たとえば「DBから取得した後のUserは必ずageが入っている」というような場面で使えます。

Step 3:Readonlyで「変更禁止のUser」を作る

Readonlyのイメージ

Readonly<T> は、

「Tのすべてのプロパティを読み取り専用にした型」

です。

ReadonlyUser型を作る

// 読み取り専用のUser
type ReadonlyUser = Readonly<User>;
TypeScript

使用例を見てみます。

const u: ReadonlyUser = {
  id: 1,
  name: "Taro",
  email: "[email protected]",
  isActive: true,
};

// u.name = "Hanako"; // コンパイルエラー:Readonlyなので変更不可
TypeScript

重要ポイント:

  • 「このUserは絶対に変更してほしくない」という意図を、 型としてはっきり表現できます。
  • ログ出力用・履歴表示用など、「参照専用」の場面でとても役に立ちます。

Step 4:Pickで「表示用の軽いUser」を作る

Pickのイメージ

Pick<T, K> は、

「Tから指定したプロパティだけを取り出した型」

です。

一覧表示やサマリー表示など、「全部はいらないけれど、これだけ欲しい」という場面にぴったりです。

UserSummary型を作る

// 一覧表示用のUserSummary(id, name, emailだけ)
type UserSummary = Pick<User, "id" | "name" | "email">;
TypeScript

UserSummary のイメージは次のようになります。

// 展開イメージ
type UserSummary = {
  id: number;
  name: string;
  email: string;
};
TypeScript

ポイント:

  • 「画面Aではこの項目だけ」「画面Bでは別の項目だけ」というように、 表示用の型を柔らかく作り分けることができます。
  • 元の User 型を変更せずに、用途ごとの軽量な型を増やせるのが魅力です。

Step 5:Omitで「作成用のUser」を作る

Omitのイメージ

Omit<T, K> は、

「Tから指定したプロパティを除いた型」

です。

作成時には「まだ存在しないプロパティ」を除きたい場面にぴったりです。

CreateUser型を作る

ユーザー作成時には、通常 id はまだありません。 そこで、「idを除いたUser」を作成用の型にします。

// idを除いたUser(作成用)
type CreateUser = Omit<User, "id">;
TypeScript

CreateUser のイメージは次のようになります。

// 展開イメージ
type CreateUser = {
  name: string;
  email: string;
  age?: number;
  isActive: boolean;
};
TypeScript

ポイント:

  • 「作成時にはidはいらない」というビジネスルールを、 型として自然に表現できます。
  • これにより、「idを手動で渡してしまう」といったミスをコンパイル時に防げます。

Step 6:Utility Typesを組み合わせて、さらに派生型を増やす

ここまでで、

  1. UpdateUserPartial(Partial)
  2. StrictUser(Required)
  3. ReadonlyUser(Readonly)
  4. UserSummary(Pick)
  5. CreateUser(Omit)

の5種類ができました。

ここからは、Utility Typesを組み合わせて、 もう少し実務寄りの派生型を作ってみます。

UpdateUser型(id必須+その他任意)

更新用の型として、「idは必須、それ以外は任意」という形を作ります。

// idだけを必須にしたいので、まずはidだけの型を作る
type UserIdOnly = Pick<User, "id">;

// id以外のプロパティを取り出す
type UserWithoutIdForUpdate = Omit<User, "id">;

// 更新用のUser型(id必須+その他任意)
type UpdateUser = UserIdOnly & Partial<UserWithoutIdForUpdate>;
TypeScript

UpdateUser のイメージは次のようになります。

// 展開イメージ
type UpdateUser = {
  id: number;        // 必須
  name?: string;
  email?: string;
  age?: number;
  isActive?: boolean;
};
TypeScript

ここが大事なポイントです:

  • Partial と Pick / Omit を組み合わせることで、 「一部だけ必須」「残りは任意」という柔らかい型を作れます。
  • 更新処理では、「どの項目を更新するかは呼び出し側に任せる」一方で、 「どのユーザーかは必ず指定してもらう」という前提を型で表現できます。

Step 7:作った型を実際に使ってみる

最後に、ここまで作った型を使って、 簡単な関数をいくつか書いてみます。

// ベースUser型
type User = {
  id: number;
  name: string;
  email: string;
  age?: number;
  isActive: boolean;
};

// 1. Partial
type UpdateUserPartial = Partial<User>;

// 2. Required
type StrictUser = Required<User>;

// 3. Readonly
type ReadonlyUser = Readonly<User>;

// 4. Pick
type UserSummary = Pick<User, "id" | "name" | "email">;

// 5. Omit
type CreateUser = Omit<User, "id">;

// 6. 組み合わせ:UpdateUser
type UserIdOnly = Pick<User, "id">;
type UserWithoutIdForUpdate = Omit<User, "id">;
type UpdateUser = UserIdOnly & Partial<UserWithoutIdForUpdate>;

// ===== 使用例 =====

// ユーザー作成関数
function createUser(data: CreateUser): User {
  const newUser: User = {
    id: Math.floor(Math.random() * 10000),
    ...data,
  };
  console.log("ユーザーを作成しました:", newUser);
  return newUser;
}

// ユーザー更新関数(UpdateUserを使う)
function updateUser(user: User, update: UpdateUser): User {
  if (user.id !== update.id) {
    throw new Error("IDが一致しません");
  }

  const updated: User = {
    ...user,
    ...update,
  };

  console.log("ユーザーを更新しました:", updated);
  return updated;
}

// UserSummaryを返す関数
function toUserSummary(user: User): UserSummary {
  return {
    id: user.id,
    name: user.name,
    email: user.email,
  };
}

// ReadonlyUserを使うログ関数
function logUser(user: ReadonlyUser): void {
  console.log("ユーザー情報(読み取り専用):", user);
}

// ===== 実際に使ってみる =====

const created = createUser({
  name: "Taro",
  email: "[email protected]",
  age: 20,
  isActive: true,
});

const updated = updateUser(created, {
  id: created.id,
  email: "[email protected]",
});

const summary = toUserSummary(updated);
console.log("UserSummary:", summary);

logUser(updated);
TypeScript

Day 24演習のまとめ

今回の演習では、

  • ひとつの既存型 User をベースにして、
    • Partial で「全部任意の更新用」
    • Required で「全部必須の厳密なUser」
    • Readonly で「変更禁止のUser」
    • Pick で「表示用の軽量User」
    • Omit で「作成用のUser」
    • 組み合わせで「id必須+その他任意のUpdateUser」 といった派生型を作りました。
  • Utility Typesは、「同じような型を何度も手書きする」手間を減らすだけでなく、 ビジネスルール(作成時はid不要、更新時はid必須など)を型として表現するための道具であることも見てきました。

この演習をベースに、ぜひ

  • Product
  • Order
  • Customer

など、別のドメインでも同じように

  • Create〜
  • Update〜
  • 〜Summary
  • Readonly〜

といった派生型をUtility Typesで作ってみてください。 そうしていくうちに、「型設計そのものが楽しくなる感覚」が、じわっと育っていくはずです。

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