はじめに
先輩の書いたコードやライブラリのソースコードを覗いたりするとPartial<T>やPick<T, K>といった見慣れない型表記に出会い、それらの理解に時間をかけてしまうことがよくあります
これらは Utility Types(ユーティリティ型) と呼ばれ、TypeScriptが標準で提供している型変換のためのツールです。
Utility Typesを理解すると、以下のメリットがあります。
- ライブラリやフレームワークのソースコードが読めるようになる
- 型安全性を保ちながら柔軟なコードが書ける
この記事では、特によく使われるUtility Types紹介します。
1. Partial<T> - すべてのプロパティをオプショナルに
Partial<T>は、型Tのすべてのプロパティをオプショナル(?付き)に変換します。
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
// 結果:
// {
// id?: number;
// name?: string;
// email?: string;
// }
更新APIで一部のフィールドだけを変更したい場合や、設定オブジェクトでデフォルト値を持つ項目を表現する際によく使われます。
2. Required<T> - すべてのプロパティを必須に
Required<T>はPartial<T>の逆で、すべてのプロパティから?を取り除いて必須にします。
interface Config {
apiUrl?: string;
timeout?: number;
retryCount?: number;
}
type RequiredConfig = Required<Config>;
// 結果:
// {
// apiUrl: string;
// timeout: number;
// retryCount: number;
// }
オプショナルな設定オブジェクトにデフォルト値を適用した後の型として使ったり、「この時点では全フィールドが確定している」ことを型で表現する際に活用できます。
3. Readonly<T> - すべてのプロパティを読み取り専用に
Readonly<T>は、すべてのプロパティにreadonly修飾子を付与します。
interface Todo {
title: string;
completed: boolean;
}
type ReadonlyTodo = Readonly<Todo>;
// 結果:
// {
// readonly title: string;
// readonly completed: boolean;
// }
const todo: ReadonlyTodo = { title: "Learn TypeScript", completed: false };
// todo.completed = true; // Error: Cannot assign to 'completed'
状態管理で変更不可なデータを扱う場合や、関数の引数を変更されたくない場合に使用します。
4. Pick<T, K> - 特定のプロパティだけを抽出
Pick<T, K>は、型Tから指定したキーKのプロパティだけを抽出した新しい型を作ります。
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
type PublicUser = Pick<User, "id" | "name" | "email">;
// 結果:
// {
// id: number;
// name: string;
// email: string;
// }
APIレスポンスで公開可能な情報だけを返す型を定義したり、一覧表示用に軽量な型を作る際に便利です。
5. Omit<T, K> - 特定のプロパティを除外
Omit<T, K>はPickの逆で、指定したキーを除外した型を作ります。
interface Article {
id: number;
title: string;
content: string;
createdAt: Date;
updatedAt: Date;
}
type CreateArticleInput = Omit<Article, "id" | "createdAt" | "updatedAt">;
// 結果:
// {
// title: string;
// content: string;
// }
新規作成リクエストで自動生成されるフィールド(id、タイムスタンプなど)を除外した型を作る場合によく使われます。
6. Record<K, T> - オブジェクト型を簡潔に定義
Record<K, T>は、キーの型がK、値の型がTであるオブジェクト型を作ります。
type Status = "pending" | "approved" | "rejected";
interface StatusConfig {
label: string;
color: string;
}
const statusConfigs: Record<Status, StatusConfig> = {
pending: { label: "審査中", color: "yellow" },
approved: { label: "承認済", color: "green" },
rejected: { label: "却下", color: "red" },
};
ステータスや言語コードなどのUnion型をキーにしたマッピングオブジェクトを定義する際に活用します。キーの追加漏れをコンパイル時に検出できるのが利点です。
7. Exclude<T, U> - Union型から除外
Exclude<T, U>は、Union型TからUに代入可能な型を除外します。
type AllColors = "red" | "green" | "blue" | "yellow";
type WarmColors = "red" | "yellow";
type CoolColors = Exclude<AllColors, WarmColors>;
// 結果: "green" | "blue"
HTTPメソッドから特定のメソッドを除外したり、イベントタイプの一部だけを許可する型を作る場合などに使えます。
8. Extract<T, U> - Union型から抽出
Extract<T, U>はExcludeの逆で、Union型TからUに代入可能な型だけを抽出します。
type AllTypes = string | number | boolean | null | undefined;
type Primitives = Extract<AllTypes, string | number | boolean>;
// 結果: string | number | boolean
複数のイベント型から特定カテゴリのイベントだけを抽出したり、許可する値を明示的に制限する際に役立ちます。
9. NonNullable<T> - null/undefinedを除外
NonNullable<T>は、型からnullとundefinedを除外します。
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// 結果: string
nullチェック後の型を明示したい場合や、配列からnullをフィルタリングした後の型を表現する際に使います。
10. ReturnType<T> - 関数の戻り値の型を取得
ReturnType<T>は、関数型Tの戻り値の型を取得します。
function createUser(name: string, age: number) {
return {
id: Math.random(),
name,
age,
createdAt: new Date(),
};
}
type User = ReturnType<typeof createUser>;
// 結果:
// {
// id: number;
// name: string;
// age: number;
// createdAt: Date;
// }
ライブラリの関数から戻り値の型を取得して再利用したり、型定義が提供されていない関数の型を推論する際に便利です。
11. Parameters<T> - 関数の引数の型を取得
Parameters<T>は、関数型Tの引数の型をタプルとして取得します。
function greet(name: string, age: number, isAdmin: boolean): string {
return `Hello, ${name}!`;
}
type GreetParams = Parameters<typeof greet>;
// 結果: [name: string, age: number, isAdmin: boolean]
type FirstParam = Parameters<typeof greet>[0]; // string
既存の関数と同じシグネチャを持つラッパー関数を作る場合や、関数の特定の引数の型だけを取り出したい場合に活用できます。
12. Awaited<T> - Promiseをアンラップ
Awaited<T>は、Promise<T>からTを取り出します。ネストしたPromiseも再帰的にアンラップします。
type A = Awaited<Promise<string>>;
// 結果: string
type B = Awaited<Promise<Promise<number>>>;
// 結果: number(ネストも解消)
ReturnTypeと組み合わせて非同期関数の実際の戻り値の型を取得する際によく使われます(Awaited<ReturnType<typeof asyncFunc>>)。
実践:複数のUtility Typesを組み合わせる
複数のUtility Typesを組み合わせて使うことが多いです。
interface User {
id: number;
name: string;
email: string;
role: "admin" | "user";
createdAt: Date;
updatedAt: Date;
}
// 編集フォームの型:id等を除外し、残りをオプショナルに
type UserEditForm = Partial<Omit<User, "id" | "createdAt" | "updatedAt">>;
// 内部設定の型:すべて必須かつ読み取り専用
type InternalConfig = Readonly<Required<Config>>;
まとめ
| Utility Type | 役割 | よくある使い方 |
|---|---|---|
Partial<T> |
全プロパティをオプショナルに | 更新処理、部分的な設定 |
Required<T> |
全プロパティを必須に | デフォルト値適用後 |
Readonly<T> |
全プロパティを読み取り専用に | 変更不可な状態 |
Pick<T, K> |
特定のプロパティを抽出 | APIレスポンスの型定義 |
Omit<T, K> |
特定のプロパティを除外 | 作成リクエストの型定義 |
Record<K, T> |
キーと値の型を指定 | マッピングオブジェクト |
Exclude<T, U> |
Union型から除外 | 特定の値を除外 |
Extract<T, U> |
Union型から抽出 | 特定の値だけ許可 |
NonNullable<T> |
null/undefinedを除外 | 値が確実に存在する場合 |
ReturnType<T> |
関数の戻り値の型を取得 | ライブラリの型再利用 |
Parameters<T> |
関数の引数の型を取得 | ラッパー関数 |
Awaited<T> |
Promiseをアンラップ | 非同期関数の戻り値 |
Utility Typesを使いこなすことで、型定義の重複を減らし、変更に強い型設計ができるようになります
書けなくても、読めることができればコードを紐解く時間を大幅に削減することができます!
余談
mosyaのType Challengesで型パズルに挑戦することができます
難易度高めなので是非挑戦してみてください