こんにちは、株式会社ファミトラでエンジニアをしているおおさわです。
ファミトラの開発では、OpenAPI の定義を起点に基本となるデータモデルの型を決めており、フロントエンド側でも原則としてその型を変形することで型エイリアスを作る運用をしています。
その中で特に活躍しているのが、今回紹介する ユーティリティ型 です。
ユーティリティ型とは?
ユーティリティ型とは、TypeScript 標準で提供されている「よくある型変換のパターン」をまとめた仕組みです。
- プロパティを全部オプショナルにしたい
- 一部のキーだけ抜き出したい
- 読み取り専用にしたい
- Union 型から不要な部分を取り除きたい
といった、自分で条件型を書けばできるけど毎回やるのは面倒なパターンを、あらかじめ提供してくれています。
この記事では、実務で特によく使うユーティリティ型を中心に、ざっと整理していきます。
(条件型については、よければ前回の記事を参考にしてください)
Partial<T>
T のプロパティを全部オプショナルにするユーティリティ型です。
type User = {
id: number;
name: string;
};
type UserUpdate = Partial<User>;
// => { id?: number; name?: string }
Required<T>
T の 全部のプロパティを必須にするユーティリティ型です。
type User = {
id: number;
name?: string;
};
type StrictUser = Required<User>;
// => { id: number; name: string }
Readonly<T>
T のプロパティを すべて読み取り専用(readonly)にします。
type User = {
id: number;
name: string;
};
type FrozenUser = Readonly<User>;
// => { readonly id: number; readonly name: string }
Pick<T, K>
T の中から、指定したキー K だけを 抜き出した新しい型を作ります。
type User = {
id: number;
name: string;
email: string;
};
type UserPublic = Pick<User, 'id' | 'name'>;
// => { id: number; name: string }
Omit<T, K>
T から K のキーを除外した型を作ります。
type User = {
id: number;
name: string;
email: string;
};
type UserWithoutEmail = Omit<User, 'email'>;
// => { id: number; name: string }
Record<K, T>
キーの集合(K)と値の型(T)からオブジェクト型を作るユーティリティ型です。
type Role = 'admin' | 'user' | 'guest';
type Permissions = Record<Role, number>;
// => { admin: number; user: number; guest: number }
Exclude<T, U>
Union 型 T から、U を 取り除いた型を作ります。
type Status = 'draft' | 'published' | 'deleted';
type VisibleStatus = Exclude<Status, 'deleted'>;
// => "open" | "closed"
Extract<T, U>
T と U に共通する型だけを 取り出した Union 型作ります。
type AllEvents = 'click' | 'hover' | 'focus' | 'submit';
type UserAllowedEvents = 'click' | 'submit';
type TrackableEvent = Extract<AllEvents, UserAllowedEvents>;
// => "click" | "submit"
NonNullable<T>
T から null と undefined を取り除いた型を作ります。
type MaybeName = string | null | undefined;
type StrictName = NonNullable<MaybeName>;
// => string
まとめ
ユーティリティ型を理解しておくと、日々の型定義が驚くほどスムーズになります。
特に弊社のように、
- バックエンドで扱うデータモデルを OpenAPI で定義
- フロントエンドでは OpenAPI から型を生成
- 必要に応じてユーティリティ型で変形して使う
というワークフローを採用している環境では、ユーティリティ型はほぼ毎日触れる基盤的な概念です。
型を一から書くのではなく、既存の型をどう再利用し、どう変換して使うかを意識できるようになると、TypeScript の強みをより活かして型安全なプログラムが書けて幸せになれます