TypeScriptTypeScript [タイプスクリプト]JavaScriptに型システムを追加した言語でアプリケーションを開発していると、「既存の型定義から一部のプロパティだけを抜き出したい」「すべてのプロパティを一時的に省略可能にしたい」といった場面に頻繁に遭遇します。
愚直に似たような型を何個も手動で定義していると、コードの重複が増えるだけでなく、元の型定義が変更された際に修正漏れが発生してバグの原因になります。
このような悩みをスマートに解決するのが、TypeScriptに標準で組み込まれている ユーティリティ型(Utility Types) です。本記事では、実務で頻出するユーティリティ型の具体的な使い方、ユニオン型や関数の型操作、さらに現場でよく使われる実践的な組み合わせパターンまで、実機検証済みのコード例を交えて詳しく解説します。
TypeScriptのユーティリティ型(Utility Types)とは?
ユーティリティ型とは、TypeScriptが標準ライブラリとして提供している組み込みの型変換ツールです。既存の型やキーなどを型引数として受け取り、プロパティの抽出・除外・修飾子の変更などを適用した新しい型を柔軟に生成できます。
なぜユーティリティ型を使うのか?(重複排除と型安全性の両立)
ユーティリティ型を活用する最大のメリットは、 型定義の二重管理(DRY原則の違反)を防ぎつつ、型安全性を高められる点 にあります。
例えば、ユーザー情報を表す User 型が存在するとき、ユーザー更新APIAPI [エーピーアイ]Application Programming Interface。異なるソフトウェア間の接続口のリクエスト型として「全プロパティが任意(オプショナル)」な型が必要になったとします。
もし手動で新しい型を定義してしまうと、元の User に新しいフィールドが追加された際に更新用インターフェースへの反映を忘れ、型の不整合が生じてしまいます。
// 元となるマスターの型定義interface User { id: string; name: string; email: string; age: number;}
// ❌ 悪い例: 手動で似たような型を二重定義する(メンテナンス性が低下)interface UserUpdateRequestManual { id?: string; name?: string; email?: string; age?: number;}
// ⭕ 良い例: ユーティリティ型 Partial を使って自動派生させるtype UserUpdateRequest = Partial<User>;ユーティリティ型を使用すれば、元の型への変更が自動的に派生型へ伝播するため、保守性と堅牢性が大幅に向上します。
実務で押さえるべきユーティリティ型の全体マップ
TypeScriptには多数のユーティリティ型が存在しますが、実務で日常的に登場するものは以下の4系統に大別できます。
- オブジェクトのプロパティを操作する型:
Partial,Required,Readonly,Pick,Omit,Record - ユニオン型(Union)の要素を操作する型:
Exclude,Extract,NonNullable - 関数のシグネチャから型を抽出する型:
ReturnType,Parameters - 非同期処理の型を操作する型:
Awaited
各型の構文と具体的なユースケースを順に見ていきましょう。
オブジェクトのプロパティを操作するユーティリティ型
実務で最も登場頻度が高いのが、オブジェクトのプロパティを変換するユーティリティ型です。
Partial<T>:全プロパティを省略可能(optional)にする
Partial<T> は、型 T に含まれるすべてのプロパティに ? を付与し、省略可能にするユーティリティ型です。
- 主なユースケース: 設定オブジェクトの部分的な上書き、エンティティの部分更新(PATCHリクエスト)
interface User { id: string; name: string; email: string; age?: number;}
// すべてのプロパティがオプショナル(?)になるtype UpdateUserPayload = Partial<User>;
// 特定のプロパティだけを渡しても型エラーにならないconst payload: UpdateUserPayload = { name: "山田太郎",};Required<T>:全プロパティを必須にする
Required<T> は、型 T に含まれるすべてのオプショナルプロパティ(? 付き)から ? を取り除き、すべてを必須プロパティに変換するユーティリティ型です。
- 主なユースケース: オプショナル設定をデフォルト値で補完した後の完全な設定オブジェクトの型付け
interface UserConfig { theme?: "light" | "dark"; autoSave?: boolean; timeout?: number;}
// すべてのプロパティが必須になるtype FinalConfig = Required<UserConfig>;
// エラー例: 必須プロパティが不足している場合// error TS2741: Property 'timeout' is missing in type '{ theme: "dark"; autoSave: true; }' but required in type 'Required<UserConfig>'.const invalidConfig: FinalConfig = { theme: "dark", autoSave: true,};Readonly<T>:全プロパティを読み取り専用にする
Readonly<T> は、型 T のすべてのプロパティに readonly 修飾子を付与し、コンパイル時に再代入を禁止するユーティリティ型です。
- 主なユースケース: 状態管理(ReduxやZustand等)でのイミュータブルな状態定義、外部公開オブジェクトの改変防止
interface AppState { currentUserId: string; isLoggedIn: boolean;}
const state: Readonly<AppState> = { currentUserId: "user_01", isLoggedIn: true,};
// コンパイルエラーが発生する// error TS2540: Cannot assign to 'isLoggedIn' because it is a read-only property.state.isLoggedIn = false;値そのものを不変値として型推論させたい場合は、 【TypeScript】as constの使い方!型を固定する実務テクニック で解説した as const アサーションと併せて活用するのが効果的です。
Pick<T, K>:指定したプロパティのみを抽出する
Pick<T, K> は、型 T から指定したキー K のプロパティだけを抜き出して新しい型を作成するユーティリティ型です。複数のキーを指定する場合は |(ユニオン型)で区切ります。
- 主なユースケース: 一覧表示用コンポーネントに渡す軽量なDTO(データ転送オブジェクト)の作成
interface Article { id: string; title: string; content: string; authorId: string; publishedAt: Date;}
// id と title だけを抽出した型を作成type ArticleCardProps = Pick<Article, "id" | "title">;
// 有効なオブジェクトconst cardData: ArticleCardProps = { id: "article-123", title: "TypeScriptユーティリティ型の使い方",};
// オブジェクトリテラルを直接代入する場合、抽出対象外のプロパティを含めるとコンパイルエラーになる// error TS2353: Object literal may only specify known properties, and 'content' does not exist in type 'ArticleCardProps'.const invalidCard: ArticleCardProps = { id: "article-123", title: "TypeScriptユーティリティ型の使い方", content: "本文データ",};オブジェクトリテラルを直接代入する場合、抽出対象外のプロパティを含めると 余剰プロパティチェック(Excess Property Checks) によってコンパイルエラーになります。ただし、既存の変数を代入する場合は、TypeScriptの構造的型付けの仕組みにより余分なプロパティが許容されることがあります。
interface Article { id: string; title: string; content: string; authorId: string; publishedAt: Date;}
type ArticleCardProps = Pick<Article, "id" | "title">;
const article: Article = { id: "article-123", title: "TypeScript入門", content: "記事本文", authorId: "user-01", publishedAt: new Date(),};
// 既存の変数を代入する場合、余分なプロパティが含まれていても代入可能const card: ArticleCardProps = article;Omit<T, K>:指定したプロパティを除外する
Omit<T, K> は、型 T から指定したキー K のプロパティを除外した新しい型を作成するユーティリティ型です。Pick のちょうど逆の挙動をします。
- 主なユースケース: IDや作成日時などのシステム自動採番フィールドを除いた、新規登録フォームの入力型
interface Product { id: string; name: string; price: number; createdAt: Date; updatedAt: Date;}
// システム生成の id, createdAt, updatedAt を除外type CreateProductInput = Omit<Product, "id" | "createdAt" | "updatedAt">;
// 新規登録時の入力データ型として安全に使用できるconst newProduct: CreateProductInput = { name: "エルゴノミクスキーボード", price: 24800,};Record<K, T>:キーと値の型を指定した辞書型オブジェクトを作成する
Record<K, T> は、キーの型 K と値の型 T を明示的に指定した辞書型オブジェクト(連想配列)を構築するユーティリティ型です。
- 主なユースケース: 列挙型やリテラル型のキーに対するマッピング表、権限管理テーブル
type UserRole = "admin" | "editor" | "viewer";
interface RoleConfig { canDelete: boolean; canEdit: boolean;}
// UserRole の各キーを漏れなく網羅した設定テーブルを定義const rolePermissions: Record<UserRole, RoleConfig> = { admin: { canDelete: true, canEdit: true }, editor: { canDelete: false, canEdit: true }, viewer: { canDelete: false, canEdit: false },};Record にリテラル型のユニオンをキーとして渡すと、指定されたすべてのキーが必須プロパティとして定義されます。そのため、 定義されているすべてのキーが揃っていない場合にコンパイルエラー となり、設定項目の定義漏れをコンパイル段階で検知できます。また、オブジェクトリテラルを直接代入する場合は、定義されていない余分なキーも余剰プロパティチェックによって検出できます(既存の変数を代入する場合は、構造的型付けにより余分なプロパティが許容されることがあります)。
ユニオン型(Union)を操作するユーティリティ型
次に、複数の型の組み合わせであるユニオン型から特定の値を取り出したり除外したりするユーティリティ型を解説します。
Exclude<T, U>:ユニオン型から特定の部分型を除外する
Exclude<T, U> は、ユニオン型 T から型 U に割り当て可能な型を取り除いた新しいユニオン型を生成します。
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
// 安全な読み取り専用メソッド(GET)を除外し、状態変更を伴うメソッドだけを抽出type MutateMethod = Exclude<HttpMethod, "GET">;// 結果: "POST" | "PUT" | "DELETE" | "PATCH"Extract<T, U>:ユニオン型から一致する部分型のみを抽出する
Extract<T, U> は、ユニオン型 T の中から型 U に割り当て可能な型だけを抜き出すユーティリティ型です。Exclude の逆の処理を行います。
type EventType = "click" | "hover" | "keydown" | "keyup" | "resize";type KeyEventType = Extract<EventType, "keydown" | "keyup" | "keypress">;// 結果: "keydown" | "keyup"NonNullable<T>:null と undefined を除外する
NonNullable<T> は、型 T から null および undefined を取り除いた型を生成します。
type NullableString = string | null | undefined;
type CleanString = NonNullable<NullableString>;// 結果: string実行時に値の有無を判定して型を絞り込みたい場合は、 【TypeScript】型ガードの使い方と型の絞り込み完全ガイド で紹介しているユーザー定義型ガード(Type Guard)と組み合わせると効果的です。
関数のシグネチャを操作するユーティリティ型
関数自体の実装から返り値の型や引数の型を逆引きして抽出できるユーティリティ型です。
ReturnType<T>:関数の戻り値の型を取得する
ReturnType<T> は、関数型 T の戻り値の型を取得します。サードパーティライブラリや既存のファクトリ関数が返す詳細なオブジェクト型を再利用したいときに重宝します。
関数そのものを渡すのではなく、 typeof 演算子と組み合わせて型として渡す 点がポイントです。
function buildUserProfile(id: string, name: string) { return { id, name, isPremium: false, createdAt: new Date(), metadata: { loginCount: 0, lastLoginIp: "127.0.0.1", }, };}
// 関数の返り値の型を自動抽出type UserProfile = ReturnType<typeof buildUserProfile>;
// 推論される型:// {// id: string;// name: string;// isPremium: boolean;// createdAt: Date;// metadata: { loginCount: number; lastLoginIp: string; };// }Parameters<T>:関数の引数の型をタプルとして取得する
Parameters<T> は、関数型 T の引数の型をタプル型(固定長配列)として抽出します。
function notifyUser(userId: string, message: string, retryCount: number) { console.log(`送信: ${userId} -> ${message} (試行: ${retryCount})`);}
// 引数の型をタプルとして取得: [userId: string, message: string, retryCount: number]type NotifyArgs = Parameters<typeof notifyUser>;
// 第1引数の型を取り出す場合type UserIdArg = NotifyArgs[0]; // string非同期処理の型を操作するユーティリティ型
Awaited<T>:Promiseなどの非同期処理の結果型を取り出す
Awaited<T> は、TypeScript 4.5で導入されたユーティリティ型で、Promise などの非同期処理(then メソッドを持つオブジェクト型)を再帰的に展開し、await した際に得られる結果の型を抽出します。
- 主なユースケース: 非同期APIクライアントや
async関数の戻り値から、解決後のデータ本体の型を取り出す
type UserResponse = Promise<{ id: string; name: string;}>;
// Promiseの内部にある型を抽出type User = Awaited<UserResponse>;// 結果:// {// id: string;// name: string;// }
// ネストされたPromiseも再帰的に展開されるtype NestedPromise = Awaited<Promise<Promise<string>>>;// 結果: string実務では ReturnType と組み合わせて Awaited<ReturnType<typeof fetchUser>> のように記述することで、async 関数が返す解決後のデータ型をそのまま再利用できます。
実務で役立つユーティリティ型の組み合わせパターン
実際の開発現場では、ユーティリティ型を単体で使うだけでなく、複数組み合わせて高度な型定義をシンプルに表現することが一般的です。
パターン1:Pick と Partial を組み合わせた「特定フィールドの部分更新」
エンティティ全体ではなく、特定のプロパティ群のみを更新対象とし、かつそれらを任意項目にしたいケースです。
interface Account { id: string; email: string; passwordHash: string; nickname: string; bio: string; avatarUrl: string;}
// ユーザーがマイページで編集可能な項目だけを Pick し、さらに Partial で任意にするtype ProfileUpdateRequest = Partial<Pick<Account, "nickname" | "bio" | "avatarUrl">>;
// 有効な更新リクエスト例(bio のみ更新)const updatePayload: ProfileUpdateRequest = { bio: "フロントエンドエンジニアです。",};パターン2:Omit と Partial を組み合わせた「ID必須・その他任意の更新型」
RESTREST [レスト]Representational State Transfer。WebAPIの設計原則 APIなどで、リソースを更新する際に「対象の識別子(ID)は必須だが、更新データは任意」という型を作りたいケースです。
interface Task { id: string; title: string; completed: boolean; priority: "low" | "medium" | "high";}
// id 以外のプロパティをオプショナルにし、必須の id と交差型(&)で合体させるtype UpdateTaskPayload = { id: string } & Partial<Omit<Task, "id">>;
// id は必須だが、変更したい項目だけを指定できるconst taskUpdate: UpdateTaskPayload = { id: "task_456", completed: true,};ユーティリティ型を使うときの注意点
ユーティリティ型を使用する際は、以下の挙動に留意してください。
1. ネストしたオブジェクトは再帰的に変換されない(浅い変換)
標準の Partial や Readonly は 浅い変換(Shallow) です。オブジェクトの直下のプロパティに対してのみ適用され、ネストした子オブジェクトのプロパティまでは変更されません。
interface Company { name: string; address: { city: string; zipCode: string; };}
type ReadonlyCompany = Readonly<Company>;
const company: ReadonlyCompany = { name: "サンプル株式会社", address: { city: "東京都千代田区", zipCode: "100-0001", },};
// ❌ 直下のプロパティの書き換えは防げる// company.name = "変更後"; // コンパイルエラー
// ⚠️ ネストされた内部オブジェクトのプロパティは変更できてしまうcompany.address.city = "大阪市北区"; // エラーにならず変更可能ネストの深い階層まで再帰的に readonly やオプショナル(?)を適用したい場合は、type-fest(ReadonlyDeep や PartialDeep)や ts-essentials(DeepReadonly や DeepPartial)などのユーティリティライブラリを導入するか、自作の再帰的マッピング型を定義する必要があります。
2. Omit に存在しないキー名を指定してもエラーにならない場合がある
Pick<T, K> の場合、型 T に存在しないプロパティ名を渡すと K extends keyof T の制約によりコンパイルエラーになります。
しかし、Omit<T, K> の型定義は K extends keyof any となっているため、誤って存在しないプロパティ名を指定しても TypeScript はコンパイルエラーを出してくれません。スペルミスに気付きにくいため注意が必要です。
interface Customer { id: string; name: string;}
// 正しく id を除外type CustomerInput1 = Omit<Customer, "id">; // { name: string }
// 存在しないキー "nonExistentKey" を指定してもコンパイルエラーにならないtype CustomerInput2 = Omit<Customer, "nonExistentKey">; // { id: string; name: string } のまま3. ReturnType と Parameters はオーバーロードの最後のシグネチャを参照する
関数に複数のオーバーロードが定義されている場合、ReturnType<T> および Parameters<T> は原則として オーバーロード宣言の最後のシグネチャ から型を抽出します(すべてのオーバーロードのユニオン型になるわけではありません)。
function getValue(value: string): string;function getValue(value: number): number;function getValue(value: string | number): string | number { return value;}
// 最後のオーバーロードシグネチャ(value: number => number)が対象になるtype ValueReturn = ReturnType<typeof getValue>; // numbertype ValueParams = Parameters<typeof getValue>; // [value: number]オーバーロードが定義された関数から型を抽出する際は、意図したシグネチャが参照されているかを確認してください。
その他の標準ユーティリティ型
本記事で解説した12個以外にも、TypeScriptには特定の用途で役立つ標準ユーティリティ型が用意されています。必要に応じて公式ドキュメントも参照しながら活用してみてください。
| ユーティリティ型 | 概要 |
|---|---|
ConstructorParameters<T> | コンストラクタ関数の引数の型をタプル型として取得 |
InstanceType<T> | コンストラクタ関数(クラス)のインスタンス型を取得 |
ThisParameterType<T> | 関数型の this パラメータの型を抽出(未指定の場合は unknown) |
OmitThisParameter<T> | 関数型から this パラメータを除去した型を生成 |
ThisType<T> | オブジェクトリテラル内の this の型を指定するマーカー型(noImplicitThis の有効化が必要) |
NoInfer<T> | ジェネリック関数の型推論で特定の位置を推論対象から除外(TS 5.4+) |
Uppercase<T> | 文字列リテラル型をすべて大文字に変換 |
Lowercase<T> | 文字列リテラル型をすべて小文字に変換 |
Capitalize<T> | 文字列リテラル型の先頭1文字を大文字に変換 |
Uncapitalize<T> | 文字列リテラル型の先頭1文字を小文字に変換 |
まとめ
TypeScriptのユーティリティ型を使いこなすことで、既存の型資産を活かしながら安全かつメンテナンス性の高いコードを記述できます。
- オブジェクト操作:
Partial(全任意)、Required(全必須)、Readonly(読み取り専用化)、Pick(抽出)、Omit(除外)、Record(辞書型オブジェクト) - ユニオン操作:
Exclude(除外)、Extract(抽出)、NonNullable(nullnull [ヌル / ナル]値が存在しないことを表す特殊な値・undefined排除) - 関数・非同期操作:
ReturnType(戻り値の型)、Parameters(引数のタプル型)、Awaited(Promise等の結果型) - 実践パターン:
PickとPartialやOmitを組み合わせてAPIリクエスト型を構築 - 注意点: 浅い変換である点、余剰プロパティチェックと構造的型付けの違い、
Omitのキー制約、オーバーロード関数の参照ルールに注意
型の重複定義を減らし、変更に強いTypeScriptコードを書くために、ぜひ日常の開発でユーティリティ型を活用してみてください。
以上で本記事の解説を終わります。
よいITライフを!
人気記事
- 1
- 2
- 3
- 4
- 5
思考の整理というテーマを、実務ですぐに使える具体的な「技術」として落とし込んだ一冊です。