1. ホーム
  2. TypeScript

【TypeScript】Utility Types(Partial・Required・Pick・Omit)の使い方|既存の型から新しい型を作る

Share

TypeScript で型を書いていると、「この interface とほとんど同じだけど、一部だけ省略可能にしたい」「API のレスポンス型から必要なプロパティだけ抜き出したい」といった場面がよく出てきます。そのたびに似た型を手で書き直すと、元の型を変更したときに直し忘れが起きます。こうした「既存の型から少し違う型を作る」作業を安全に行うために用意されているのがユーティリティ型(Utility Types)です。この記事では、よく使う PartialRequiredReadonlyPickOmit の5つを取り上げ、それぞれが何をするのか、フォームの部分更新や API レスポンスの加工といった実務の場面でどう使うのかを、初心者〜中級者向けに解説します。

ユーティリティ型とは

ユーティリティ型とは、TypeScript に最初から組み込まれている「型を受け取って、加工した別の型を返す」仕組みです。関数が値を受け取って別の値を返すように、ユーティリティ型は型を受け取って別の型を返します。Partial<User> のように、山かっこ(<>)の中へ加工したい型を渡して使います。この山かっこで型を渡す書き方はジェネリクスと呼ばれ、ユーティリティ型はいわば「よく使う型変換をあらかじめ用意してくれた便利セット」です。

まずは元になる型を用意します。以下ではユーザー情報を表す User という interface を例に、各ユーティリティ型がこの型をどう変換するのかを見ていきます。

user.ts
// この記事で共通して使う元の型
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

代表的なユーティリティ型と、それぞれが何をするのかを先に一覧で確認しておきましょう。T は「加工したい元の型」、K は「対象にするプロパティ名」を表します。

ユーティリティ型何をするか
Partial<T>T のすべてのプロパティを省略可能(任意)にする
Required<T>T のすべてのプロパティを必須にする(Partial の逆)
Readonly<T>T のすべてのプロパティを読み取り専用にする
Pick<T, K>T から K で指定したプロパティだけを取り出す
Omit<T, K>T から K で指定したプロパティを取り除く

Partial:すべてのプロパティを省略可能にする

Partial<T> は、渡した型のすべてのプロパティを「あってもなくてもよい(省略可能)」に変えた型を返します。プロパティ名のうしろに ? が付いた状態と同じです。元の User は4つのプロパティすべてが必須ですが、Partial<User> ではすべてが任意になります。

partial.ts
// Partial<User> はこう展開される
// {
//   id?: number;
//   name?: string;
//   email?: string;
//   age?: number;
// }

// 一部のプロパティだけ持つオブジェクトも代入できる
const draft: Partial<User> = { name: '田中' }; // OK
const empty: Partial<User> = {};              // OK

フォームの部分更新に Partial を使う

Partial が特に役立つのが、既存のデータを「一部だけ更新する」処理です。プロフィール編集フォームで名前だけ、あるいはメールアドレスだけを変更したいとき、更新用の引数はすべてのプロパティを必須にすると不便です。Partial<User> を使えば、変更したいプロパティだけを渡せる関数を型安全に書けます。

update-user.ts
// 既存のユーザーに、変更分だけを上書きして返す
function updateUser(user: User, changes: Partial<User>): User {
  // スプレッド構文で既存の値に changes を上書き
  return { ...user, ...changes };
}

const current: User = {
  id: 1,
  name: '田中',
  email: 'tanaka@example.com',
  age: 28,
};

// 変更したいプロパティだけ渡せる
const updated = updateUser(current, { age: 29 });
// { id: 1, name: '田中', email: 'tanaka@example.com', age: 29 }

changesPartial<User> なので { age: 29 } のように一部だけ渡せますが、{ age: 'young' } のように型が合わない値を渡せばエラーになります。「渡すプロパティは自由、ただし渡した値の型は厳密にチェックされる」という状態を作れるのが Partial の便利なところです。

Partial を使うと必須チェックが効かなくなる場面

便利な Partial ですが、すべてのプロパティが省略可能になるということは、裏を返せば「本来必須のプロパティを書き忘れてもエラーにならない」という意味でもあります。たとえば新規ユーザーを作る処理でうっかり Partial<User> を引数の型にしてしまうと、idemail が無いオブジェクトも通ってしまい、必須プロパティの入力漏れをコンパイラが検出できません。

Partial はあくまで「更新」のように一部だけを扱う処理に向いています。新規作成のように「すべての値がそろっていること」を保証したい場面では、元の User をそのまま使うか、次に説明する Required を使い分けるのがよいでしょう。ユーティリティ型は便利さと引き換えにチェックを緩めることがある、という点は意識しておくと安全です。

Required:すべてのプロパティを必須にする

Required<T>Partial のちょうど逆で、省略可能だったプロパティも含めてすべてを必須に変えた型を返します。もともと ? が付いていた任意プロパティを「必ず存在する」状態にしたいときに使います。

required.ts
// 任意プロパティを持つ設定型
interface Options {
  color?: string;
  size?: number;
}

// すべてを必須にした型
type FullOptions = Required<Options>;
// { color: string; size: number; }

// color と size の両方が必要になる
const config: FullOptions = { color: 'red', size: 10 }; // OK
// const ng: FullOptions = { color: 'red' }; // エラー: size がない

ユーザーが省略した設定にあとからデフォルト値を補い、「以降はすべての値が確実にそろっている」ことを型で表現したいときなどに役立ちます。Partial で受け取り、デフォルト値とマージしてから Required の型として扱う、という流れはよくあるパターンです。

Readonly:プロパティを書き換え不可にする

Readonly<T> は、すべてのプロパティを読み取り専用(再代入不可)にした型を返します。一度作ったオブジェクトの中身をあとから変更してほしくないとき、たとえば設定値や定数的に扱うデータに使います。

readonly.ts
const user: Readonly<User> = {
  id: 1,
  name: '田中',
  email: 'tanaka@example.com',
  age: 28,
};

console.log(user.name); // 読み取りはできる
// user.name = '佐藤';  // エラー: 読み取り専用プロパティには代入できない

注意したいのは、Readonly が効くのは一番外側のプロパティだけだという点です。プロパティの値がさらにオブジェクトや配列だった場合、その中身までは読み取り専用になりません(これを「浅い(shallow)」な変換と呼びます)。ネストした値まで固定したい場合は、対象のプロパティにもう一度 Readonly を適用するなどの工夫が必要です。

Pick:必要なプロパティだけを取り出す

Pick<T, K> は、型 T の中から K で指定したプロパティだけを抜き出した新しい型を作ります。K には取り出したいプロパティ名を、リテラル型やユニオン型('id' | 'name')で指定します。

pick.ts
// User から id と name だけを取り出す
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }

const preview: UserPreview = { id: 1, name: '田中' }; // OK
// email や age は含まれないので書くとエラーになる

一覧表示のように「ID と名前しか使わない」画面用の型を、元の User から派生させて作れます。元の型を直接書き写していないため、Username の型を変えれば UserPreview にも自動的に反映されます。これがユーティリティ型の大きな利点で、型の定義を一箇所にまとめ、派生型を自動的に追従させられます。

Omit:不要なプロパティを取り除く

Omit<T, K>Pick と逆の発想で、K で指定したプロパティを取り除いた残りの型を作ります。「ほとんど元の型と同じで、一部だけ除きたい」ときは、残す方を全部並べる Pick より、除く方だけ書ける Omit のほうが簡潔です。

omit.ts
// 新規作成時は id をサーバーが採番するので、入力から除きたい
type NewUser = Omit<User, 'id'>;
// { name: string; email: string; age: number; }

// id を含まない入力を受け取り、サーバー側で id を付与する想定
function createUser(input: NewUser): User {
  const id = Math.floor(Math.random() * 10000); // 仮の採番
  return { id, ...input };
}

const created = createUser({
  name: '佐藤',
  email: 'sato@example.com',
  age: 34,
});

「新規作成時はまだ id が存在しない」というのは実務で頻出のパターンです。Omit<User, 'id'> のように書いておけば、User にプロパティが増えても id を除いた型が自動で追従します。複数のプロパティを除きたいときは Omit<User, 'id' | 'age'> のようにユニオン型で並べます。

Pick と Omit の使い分け

PickOmit はどちらも「元の型の一部を使う」ための型で、結果として同じ型を作ることもできます。判断の目安は、残したいプロパティが少なければ Pick、除きたいプロパティが少なければ Omit、と「書く量が少なくて済むほう」を選ぶことです。次の2つは同じ型になります。

pick-vs-omit.ts
// 「id を除く」を、除く側から書くか残す側から書くかの違い
type A = Omit<User, 'id'>;                    // id を除く
type B = Pick<User, 'name' | 'email' | 'age'>; // id 以外を並べる
// A と B は同じ型({ name: string; email: string; age: number; })

実務では、あとから User にプロパティが増える可能性を考えると Omit のほうが安全な場面が多くなります。Pick は「増えたプロパティを取り込まない」ため、増えても除外側に自動で回る Omit と挙動が変わります。どちらの挙動を期待するかで選ぶとよいでしょう。

ユーティリティ型は組み合わせられる

ユーティリティ型は入れ子にして組み合わせられます。たとえば「id を除いたうえで、残りをすべて省略可能にした型」は PartialOmit を重ねて表現できます。API の更新エンドポイントに送るデータ型としてよく登場する形です。

combine.ts
// id は変更させず、それ以外を任意で更新できる型
type UserUpdate = Partial<Omit<User, 'id'>>;
// { name?: string; email?: string; age?: number; }

const patch: UserUpdate = { email: 'new@example.com' }; // OK

このように既存の型を出発点にして、必要な変換を重ねていけば、似た型を何度も手書きせずに済みます。型の「単一の出どころ」を User に保てるため、仕様変更にも強い設計になります。

まとめ

ユーティリティ型は、既存の型を出発点にして少し違う型を安全に作るための組み込みツールです。全プロパティを省略可能にする Partial、逆に必須化する Required、書き換え不可にする Readonly、必要なプロパティだけ取り出す Pick、不要なプロパティを除く Omit の5つを押さえておけば、フォームの部分更新や API レスポンスの加工といった日常的な場面のほとんどをカバーできます。共通するのは「型の定義を一箇所にまとめ、派生型を自動的に追従させる」という考え方です。まずは手元の interfacePartialOmit を当ててみて、どんな型になるかをエディタで確認してみてください。

参考ページ