【TypeScript】ユーティリティ型入門:Partial・Pick・Omitの使い方

JavaScript

こんにちは、かつコーチです。

前回はジェネリクスの制約(extends)を扱い、型パラメータに条件をつける方法を紹介しました。

今回は、その仕組みを使って作られているユーティリティ型(既存の型を加工して新しい型を作る組み込み機能)を紹介します。

「同じようなインターフェースを何個も定義していて、正直コピペが多い」と感じている人にこそ読んでほしい内容です。

ユーティリティ型とは?

既存の型を”加工”するための機能

ユーティリティ型とは、既存の型を元に「一部だけ変えた型」を自動生成してくれる、TypeScriptに標準で用意された仕組みです。

型定義を1から書き直すのではなく、既存の型を材料にして加工するイメージを持つと理解しやすいです。

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

この User 型があるとき、「更新フォーム用に全部オプショナルにしたい」「一覧表示用に一部プロパティだけ欲しい」といった場面が実務では頻繁に出てきます。

そのたびに似たインターフェースを手書きしていると、片方だけ修正し忘れる、といった事故が起きやすくなります。

なぜ必要なのか

ユーティリティ型を使うと、元の型を1箇所で管理しながら、用途ごとの派生型を安全に作れます。

User の定義を直したときに、派生型も自動で追随してくれるのが最大のメリットです。

Partial:全プロパティを任意にする

基本の使い方

Partial<T> は、指定した型のすべてのプロパティをオプショナル(値がなくてもよい、任意のプロパティ)に変換します。

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

function updateUser(id: number, changes: Partial<User>): void {
  console.log(`ユーザー${id}を更新: `, changes);
}

updateUser(1, { name: "かつコーチ" }); // name だけでOK
updateUser(2, { email: "new@example.com" }); // email だけでもOK

更新処理では「変更したいプロパティだけ渡したい」ことがほとんどなので、Partial<User> にしておけば、必須プロパティを毎回全部渡す必要がなくなります。

つまずきやすいポイント:Partialの使いすぎ

❌ Before:新規作成の引数までPartialにしてしまう

function createUser(user: Partial<User>): void {
  console.log(user.id.toString()); // エラー:Object is possibly 'undefined'.
}

私が実際にやってしまったのが、新規作成用の関数にまで Partial<User> を使ってしまうミスです。

「更新のときに便利だったから」という理由だけで安易に使い回した結果、idundefined かもしれない扱いになり、意図と違うエラーに悩まされました。

✅ After:用途に応じて元の型とPartialを使い分ける

function createUser(user: User): void {
  console.log(user.id.toString()); // OK:User は id が必須
}

function updateUser(id: number, changes: Partial<User>): void {
  console.log(`ユーザー${id}を更新: `, changes);
}

「新規作成=全項目必須」「更新=一部項目だけでよい」というように、業務上の意味に合わせて型を選び直すのが正しい使い方でした。

Pick:一部プロパティだけ取り出す

基本の使い方

Pick<T, キー名> は、指定した型から特定のプロパティだけを取り出した型を作ります。

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

type UserPreview = Pick<User, "id" | "name">;

const preview: UserPreview = {
  id: 1,
  name: "かつコーチ",
};

一覧画面では password のような機密情報を扱いたくないケースが多く、Pick で必要なプロパティだけに絞った型を用意しておくと、うっかり不要なデータを扱う事故を防げます。

複数プロパティを組み合わせる

キー名は |(ユニオン型)でつなげることで、複数指定できます。

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

const contact: UserContact = {
  name: "かつコーチ",
  email: "info@example.com",
};

必要なプロパティだけを列挙するので、型定義を見ただけで「この画面はどの情報を扱うのか」が一目で分かるようになります。

Omit:一部プロパティだけ除外する

基本の使い方

Omit<T, キー名>Pick の逆で、指定したプロパティ以外を残した型を作ります。

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

type UserWithoutPassword = Omit<User, "password">;

const publicUser: UserWithoutPassword = {
  id: 1,
  name: "かつコーチ",
  email: "info@example.com",
};

プロパティ数が多い型から「これだけ除きたい」というときは、Pick で残す項目を全部書き並べるより、Omit で除外したい項目だけ書くほうが圧倒的に楽です。

PickとOmitの使い分け

ユーティリティ型向いている場面
Pick<T, K>元の型のプロパティ数が多く、必要な項目が少数のとき
Omit<T, K>元の型のプロパティのうち、除きたい項目が少数のとき

どちらも本質的にはプロパティの取捨選択なので、「残したい数」と「除きたい数」のどちらが少ないかで選ぶと、コードがすっきりします。

応用・一歩先の使い方

Partial・Pick・Omitを組み合わせる

これら3つは組み合わせて使うこともできます。

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

type UserUpdateForm = Partial<Omit<User, "id" | "password">>;

const form: UserUpdateForm = {
  name: "新しい名前", // email は省略可能
};

Omit<User, "id" | "password"> で更新不可なプロパティを除いたあと、Partial で全体を任意にすることで、「更新フォーム用の型」を1行で表現できます。

元の User 型を直接いじらずに、目的に応じた派生型を安全に作れるのが、ユーティリティ型の強みです。

Requiredで逆にオプショナルを必須へ

余談ですが、Partial の逆にあたる Required<T> もよく使います。

interface Options {
  timeout?: number;
  retry?: number;
}

function runTask(options: Required<Options>): void {
  console.log(`timeout: ${options.timeout}, retry: ${options.retry}`);
}

「デフォルト値を適用したあとは、必ず全プロパティが埋まっている」ことを型で保証したいときに Required<T> が役立ちます。

まとめ

この記事のポイント

  • Partial<T> はすべてのプロパティを任意に変換する(更新処理と相性がよい)
  • Pick<T, K> は指定したプロパティだけを取り出す
  • Omit<T, K> は指定したプロパティ以外を残す
  • 用途と関係なく便利だからとPartialを使い回すと、必須のはずのプロパティまでundefined扱いになり事故のもとになる
  • 複数のユーティリティ型は組み合わせて使え、目的に応じた派生型を1行で表現できる

次に読むべき記事

ユーティリティ型で型を加工できるようになったら、次はunion型を安全に扱うための「型ガード」を学んでいきましょう。

→ 次の記事:型ガードでunion型を安全に絞り込む

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