T-CREATOR

<div />

TypeScriptのユーティリティ型を早見表で使いこなす Partial Pick Omitの実践活用

2026年1月21日
TypeScriptのユーティリティ型を早見表で使いこなす Partial Pick Omitの実践活用

TypeScript で型安全な開発を進めるなかで、「同じような型を何度も定義している」「API のリクエストとレスポンスで微妙に異なる型が必要」といった悩みを抱えていないでしょうか。この記事では、Utility Types(ユーティリティ型)の中でも実務で使用頻度の高い Partial・Pick・Omit を中心に、チートシート形式で整理しながら、型設計の判断基準を解説します。実際に業務で採用・不採用を決めた経験をもとに、Mapped Types の仕組みから実践的な組み合わせパターンまで、初学者から実務者まで役立つ内容をまとめました。

TypeScript Utility Types 早見表

まず、よく使う Utility Types を用途別に整理した早見表を示します。

型名用途変換内容主な使用場面
Partial<T>全プロパティをオプショナル化{ a: string }{ a?: string }フォーム入力、PATCH API
Required<T>全プロパティを必須化{ a?: string }{ a: string }バリデーション後の型
Pick<T, K>指定プロパティのみ抽出{ a, b, c }{ a, b }API レスポンス、表示用型
Omit<T, K>指定プロパティを除外{ a, b, c }{ c }新規作成 API(id 除外)
Readonly<T>全プロパティを読み取り専用化{ a: string }{ readonly a: string }設定値、定数オブジェクト
Record<K, V>キーと値の型を指定した辞書型Record<string, number>マップ構造、集計データ
Exclude<T, U>ユニオン型から特定の型を除外'a' | 'b' | 'c''a' | 'b'状態遷移、enum 風の絞り込み
Extract<T, U>ユニオン型から特定の型を抽出'a' | 'b' | 'c''c'共通部分の抽出
NonNullable<T>null・undefined を除外string | nullstringnull 安全な処理
ReturnType<T>関数の戻り値型を取得() => stringstring既存関数との型整合
Parameters<T>関数の引数型をタプルで取得(a: string, b: number) => void[string, number]ラッパー関数の作成

それぞれの詳細な使い方と判断基準は後述します。

検証環境

  • OS: macOS Sequoia 15.2
  • Node.js: 22.13.0
  • TypeScript: 5.7.3
  • 主要パッケージ:
    • @types/node: 22.10.7
  • 検証日: 2026 年 01 月 21 日

Utility Types が必要になる背景

TypeScript による型安全な開発では、同じデータ構造に対して「少しだけ異なる型」が必要になる場面が頻繁に発生します。

mermaidflowchart LR
  base["User 型<br/>id, name, email"] --> create["作成用<br/>id なし"]
  base --> update["更新用<br/>全て optional"]
  base --> display["表示用<br/>name, email のみ"]

上図のように、1 つの基本型から複数のバリエーションが派生するケースは珍しくありません。

たとえば、ユーザー情報を扱うシステムでは以下のような型が必要になります。

  • 新規作成時: id は自動採番されるため不要
  • 更新時: 変更するフィールドだけを送信するため、すべてオプショナル
  • 一覧表示時: email は個人情報なので含めない

これらを個別に定義すると、以下のような問題が起きます。

typescript// ❌ 問題のある実装:同じ構造を複数回定義
interface User {
  id: number;
  name: string;
  email: string;
}

interface CreateUserRequest {
  name: string;
  email: string;
}

interface UpdateUserRequest {
  name?: string;
  email?: string;
}

interface UserListItem {
  id: number;
  name: string;
}

実際に検証したところ、この方法では User 型にプロパティを追加したとき、関連する 3 つの型すべてを手動で修正する必要がありました。プロパティが 10 個以上ある型では修正漏れが頻発し、型の不整合によるランタイムエラーにつながった経験があります。

つまずきやすい点: 型定義の重複は一見問題なく動作するため、プロジェクトが大きくなるまで気づきにくい。

型定義の重複が引き起こす課題

型定義を重複させることで発生する問題を整理します。

保守性の低下

基本となる型に変更が入ると、派生型すべてを手動で修正する必要があります。業務で問題になったのは、プロパティ名の変更(namedisplayName)を行った際に、派生型の一部で古い名前が残り、コンパイルは通るものの実行時に undefined が返るバグが発生したケースです。

型の不整合

コピー&ペーストで型を作成すると、元の型と派生型の間で微妙な違いが生まれます。

typescript// 元の型
interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date; // 後から追加
}

// 派生型(更新漏れ)
interface CreateUserRequest {
  name: string;
  email: string;
  // createdAt が漏れている
}

認知負荷の増加

型定義が増えるほど、「この場面ではどの型を使うべきか」の判断が難しくなります。実務では、チームメンバーが独自の派生型を作成し、同じ目的の型が複数存在する状態になったことがあります。

mermaidflowchart TD
  problem["型定義の重複"] --> maintain["保守性の低下"]
  problem --> inconsist["型の不整合"]
  problem --> cognitive["認知負荷の増加"]
  maintain --> bug["修正漏れによるバグ"]
  inconsist --> runtime["ランタイムエラー"]
  cognitive --> duplicate["重複型の乱立"]

これらの課題を解決するのが Utility Types です。Mapped Types の仕組みを活用し、基本型から派生型を自動生成することで、型の一貫性を保ちながら保守性を高められます。

Partial・Pick・Omit の使い分けと判断基準

ここでは、実務で最も使用頻度の高い 3 つの Utility Types について、採用・不採用の判断基準を解説します。

Partial:全プロパティをオプショナル化

Partial<T> は、型 T のすべてのプロパティをオプショナル(省略可能)にします。

typescriptinterface User {
  id: number;
  name: string;
  email: string;
}

// 全プロパティがオプショナルになる
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }

採用すべき場面

  • PATCH API のリクエスト型: 更新したいフィールドだけを送信する
  • フォームの入力途中状態: すべての項目が埋まっていない状態を許容する
  • デフォルト値とのマージ: 一部だけ上書きしたい設定オブジェクト

以下は動作確認済みのコード例です。

typescript// PATCH API のリクエスト型として使用
function updateUser(id: number, updates: Partial<Omit<User, "id">>) {
  // updates は { name?: string; email?: string }
  console.log(`Updating user ${id}:`, updates);
}

updateUser(1, { name: "新しい名前" }); // OK
updateUser(1, {}); // OK(何も更新しない)

採用しなかった場面

検証の結果、以下のケースでは Partial の採用を見送りました。

  • 必須項目が明確に存在する場合: たとえば id は必須だが他はオプショナルという型では、Partial だけでは表現できない
  • ネストしたオブジェクトを含む場合: Partial は浅い(shallow)変換のため、ネストした部分はオプショナルにならない
typescriptinterface NestedUser {
  id: number;
  profile: {
    name: string;
    bio: string;
  };
}

type PartialNestedUser = Partial<NestedUser>;
// profile 自体はオプショナルになるが、
// profile.name や profile.bio は必須のまま

const user: PartialNestedUser = {
  profile: { name: "テスト" }, // ❌ bio が必須なのでエラー
};

つまずきやすい点: Partial はネストしたオブジェクトの中身までオプショナルにしない。深い階層まで対応するには DeepPartial を自作する必要がある。

Pick:指定プロパティのみ抽出

Pick<T, K> は、型 T から指定したプロパティ K だけを抽出します。

typescript// name と email だけを抽出
type UserProfile = Pick<User, "name" | "email">;
// { name: string; email: string; }

採用すべき場面

  • API レスポンスの絞り込み: 必要なフィールドだけを返す
  • 表示用コンポーネントの Props: 表示に必要な情報だけを受け取る
  • セキュリティ上の理由で一部だけ公開: 内部用の型から公開用の型を生成
typescript// 一覧表示用(email は個人情報なので含めない)
type UserListItem = Pick<User, 'id' | 'name'>;

// コンポーネントの Props として使用
function UserCard({ id, name }: UserListItem) {
  return <div>{name}</div>;
}

採用しなかった場面

  • 除外したいプロパティが少ない場合: 10 個のプロパティから 8 個を選ぶなら、Omit で 2 個除外する方が簡潔
  • 動的にプロパティを決定する場合: Pick の第 2 引数は静的な文字列リテラルのユニオン型である必要がある

Omit:指定プロパティを除外

Omit<T, K> は、型 T から指定したプロパティ K を除外します。

typescript// id を除外(新規作成用)
type CreateUserRequest = Omit<User, "id">;
// { name: string; email: string; }

採用すべき場面

  • 新規作成 API のリクエスト型: idcreatedAt など自動生成されるフィールドを除外
  • 継承元の型から不要なプロパティを除去: 外部ライブラリの型をカスタマイズ
typescript// 新規作成API(id と createdAt は自動生成)
interface FullUser {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
  updatedAt: Date;
}

type CreateUserRequest = Omit<FullUser, "id" | "createdAt" | "updatedAt">;
// { name: string; email: string; }

Pick と Omit の使い分け判断基準

状況推奨する型理由
抽出したいプロパティが少ないPick列挙が簡潔
除外したいプロパティが少ないOmit列挙が簡潔
プロパティ数が同程度Omit新規プロパティ追加時に自動で含まれる
セキュリティ上、明示的に許可したいPickホワイトリスト方式で安全

実際に試したところ、Omit は「新しいプロパティが追加されたとき自動で含まれる」という特性があるため、意図しないプロパティが含まれるリスクがあります。機密情報を扱う API では Pick でホワイトリスト方式を採用する方が安全です。

実務で使える組み合わせパターン

Utility Types は単独で使うだけでなく、組み合わせることでより柔軟な型設計が可能です。

パターン 1:Partial と Omit の組み合わせ(PATCH API)

id は必須で、それ以外は任意で更新できる型を作成します。

typescriptinterface User {
  id: number;
  name: string;
  email: string;
  role: "admin" | "user";
}

// id 以外をオプショナルに
type UpdateUserRequest = Partial<Omit<User, "id">> & Pick<User, "id">;

function updateUser(request: UpdateUserRequest) {
  console.log(`Updating user ${request.id}`);
  // request.name, request.email, request.role はすべてオプショナル
}

updateUser({ id: 1, name: "新しい名前" }); // OK
updateUser({ id: 1 }); // OK(何も更新しない)
// updateUser({ name: '名前' }); // ❌ id が必須なのでエラー

パターン 2:Pick と Required の組み合わせ

オプショナルなプロパティを含む型から、特定のプロパティを必須化して抽出します。

typescriptinterface Config {
  apiUrl?: string;
  timeout?: number;
  retryCount?: number;
  debug?: boolean;
}

// apiUrl と timeout は必須、他は不要
type RequiredConfig = Required<Pick<Config, "apiUrl" | "timeout">>;
// { apiUrl: string; timeout: number; }

パターン 3:Record と Pick の組み合わせ(辞書型)

特定のキーだけを持つ辞書型を作成します。

typescripttype UserRole = "admin" | "editor" | "viewer";

// 各ロールの権限を定義
type Permissions = Record<
  UserRole,
  {
    canRead: boolean;
    canWrite: boolean;
    canDelete: boolean;
  }
>;

const permissions: Permissions = {
  admin: { canRead: true, canWrite: true, canDelete: true },
  editor: { canRead: true, canWrite: true, canDelete: false },
  viewer: { canRead: true, canWrite: false, canDelete: false },
};

カスタム Utility Types の作成

標準の Utility Types では対応できないケースでは、Mapped Types を活用してカスタム型を作成します。

DeepPartial:ネストしたオブジェクトも再帰的にオプショナル化

typescripttype DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object
    ? T[P] extends Array<infer U>
      ? Array<DeepPartial<U>>
      : DeepPartial<T[P]>
    : T[P];
};

interface NestedConfig {
  server: {
    host: string;
    port: number;
    ssl: {
      enabled: boolean;
      cert: string;
    };
  };
  database: {
    url: string;
  };
}

// すべてのネストしたプロパティがオプショナルに
type PartialConfig = DeepPartial<NestedConfig>;

const config: PartialConfig = {
  server: {
    ssl: {
      enabled: true,
      // cert は省略可能
    },
  },
  // database は省略可能
};

PickByType:特定の型を持つプロパティだけを抽出

typescripttype PickByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface MixedData {
  id: number;
  name: string;
  count: number;
  isActive: boolean;
  description: string;
}

// number 型のプロパティだけを抽出
type NumberProps = PickByType<MixedData, number>;
// { id: number; count: number; }

// string 型のプロパティだけを抽出
type StringProps = PickByType<MixedData, string>;
// { name: string; description: string; }

Mutable:Readonly を解除

外部ライブラリが返す Readonly な型を変更可能にしたい場合に使用します。

typescripttype Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

interface ReadonlyUser {
  readonly id: number;
  readonly name: string;
}

type MutableUser = Mutable<ReadonlyUser>;
// { id: number; name: string; } (readonly が解除される)

Utility Types 詳細比較まとめ

実務での採用判断に役立つよう、詳細な比較表を示します。

型名向いているケース向かないケース注意点
Partial<T>PATCH API、フォーム入力途中必須項目がある場合、ネストしたオブジェクト浅い変換のみ
Required<T>バリデーション後、設定の確定一部だけ必須化したい場合すべてが必須になる
Pick<T, K>明示的に許可したいプロパティがある抽出したいプロパティが多いホワイトリスト方式
Omit<T, K>除外したいプロパティが少ない機密情報を含む型新規プロパティが自動で含まれる
Readonly<T>設定値、定数、イミュータブルなデータ更新が必要なオブジェクト浅い変換のみ
Record<K, V>辞書型、マップ構造動的にキーが増減する場合キーの型が固定される

判断フローチャート

mermaidflowchart TD
  start["型の変換が必要"] --> q1{"プロパティを<br/>増やす?減らす?"}
  q1 -->|減らす| q2{"多くを残す?<br/>少しだけ残す?"}
  q2 -->|多くを残す| omit["Omit を使用"]
  q2 -->|少しだけ残す| pick["Pick を使用"]
  q1 -->|変更する| q3{"オプショナル?<br/>必須?<br/>読み取り専用?"}
  q3 -->|オプショナル| partial["Partial を使用"]
  q3 -->|必須| required["Required を使用"]
  q3 -->|読み取り専用| readonly["Readonly を使用"]
  q1 -->|新しい構造| record["Record を使用"]

上図は型変換の方針を決める際の判断フローです。まず「プロパティを増やすか減らすか」を考え、次に具体的な Utility Types を選択します。

まとめ

TypeScript の Utility Types は、型定義の重複を排除し、型安全な開発を効率化するための重要な機能です。

  • Partial は PATCH API やフォームの入力途中状態に適している
  • Pick はセキュリティ上、明示的に許可したいプロパティがある場合に有効
  • Omit は除外したいプロパティが少ない場合に簡潔に書ける
  • 組み合わせることで、より複雑な型変換にも対応できる
  • 標準の Utility Types で足りない場合は、Mapped Types を活用してカスタム型を作成する

ただし、Utility Types を過度に組み合わせると可読性が低下するため、チーム内で「どの程度の複雑さまで許容するか」のガイドラインを設けることを推奨します。実際の業務では、3 つ以上の Utility Types を組み合わせる場合は型エイリアスで名前をつけ、意図を明確にするルールを採用しています。

型設計は「正解が 1 つではない」領域です。プロジェクトの規模やチームの習熟度に応じて、適切な抽象度を選択してください。

関連リンク

著書

とあるクリエイター

フロントエンドエンジニア Next.js / React / TypeScript / Node.js / Docker / AI Coding

;