TypeScript

TypeScriptのユーティリティ型で似た型を手書きしない

読了 6分
TypeScript

TypeScriptのユーティリティ型で似た型を手書きしない

似たような型を毎回手書きしていて、フィールドを1つ足すたびにあちこち直す羽目になって修正漏れが出たので、ユーティリティ型を使うようにしたときのメモです。

TypeScriptには既存の型から新しい型を導く ユーティリティ型 が標準で用意されていて、これを使うと「元の型が変われば派生型も自動で追従する」状態を作れます。ここでは自分がよく使う4つを整理します。

題材として、次のユーザー型を共通の元データとして使います。

type User = {
  id: string
  name: string
  email: string
  age: number
  createdAt: Date
}

Partial:すべてを任意にする

Partial<T> は、型 T のすべてのプロパティを省略可能(? 付き)にします。「一部だけ更新する」関数の引数によく合います。

function updateUser(id: string, changes: Partial<User>) {
  // changes は { name?: string; email?: string; ... } のように全部任意
}

updateUser('u1', { email: 'new@example.com' }) // OK:一部だけでよい

更新APIのように「渡されたフィールドだけ反映する」処理で重宝します。逆に、すべてを必須化したいときは Required<T> を使います。

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

Pick<T, Keys> は、型から指定したプロパティだけを抜き出します。一覧表示用に「最小限のフィールドだけ持つ型」を作りたいときに便利です。

type UserListItem = Pick<User, 'id' | 'name'>
// => { id: string; name: string }

元の User に項目が増えても、UserListItemidname だけのまま保たれます。

Omit:特定のプロパティを除外する

Omit<T, Keys>Pick の逆で、指定したプロパティを 除いた 型を作ります。「サーバーが自動で付与するフィールドは、作成時の入力には含めたくない」といったケースにぴったりです。

type CreateUserInput = Omit<User, 'id' | 'createdAt'>
// => { name: string; email: string; age: number }

idcreatedAt はサーバー側で決まるので、入力フォームの型からは外しておく、という設計が型で表現できます。

PickOmit の使い分けの目安はこんな感じです。

状況向いている型
残したい項目が少ないPick
除きたい項目が少ないOmit

Record:キーと値の対応を作る

Record<Keys, Value> は、決まったキー集合に対して同じ型の値を持つオブジェクトを表します。区分ごとの設定をまとめるときによく使います。

type Role = 'admin' | 'editor' | 'viewer'

const permissions: Record<Role, boolean> = {
  admin: true,
  editor: true,
  viewer: false,
}

Record<Role, boolean> としておくと、Role に新しい値を足したときに「キーが足りない」とコンパイルエラーで気づけます。設定の書き漏れを型で防げるのが大きな利点です。これは便利。

組み合わせて使う

ユーティリティ型は組み合わせると効果が一段と高まります。たとえば「更新フォーム用に、一部のフィールドだけを任意にした型」はこう書けます。

// プロフィール編集:name と email だけ、しかも任意で受け取る
type ProfileUpdate = Partial<Pick<User, 'name' | 'email'>>
// => { name?: string; email?: string }

こんなふうに小さな型を重ねていくと、元の User を1か所直すだけで関連する型がすべて追従します。型の定義が増えても、真実の源(source of truth)が1つに保たれるわけです。

まとめ

  • Partial / Required … 任意・必須を一括で切り替える
  • Pick / Omit … 必要な、あるいは不要なプロパティを選ぶ
  • Record … キー集合に対する値のマップを作り、書き漏れを防ぐ

まずは「同じようなオブジェクト型を2つ以上書きそうになったら、片方を派生にできないか」を考えるところから始めてみるとよさそうです。標準のユーティリティ型はTypeScript公式ハンドブックに一覧があるので、手元に開いておくと迷いません。以上です。