複数のTypeScriptユーティリティ型の合成

Partial、Pick、Omit、Required、Readonly、その他のユーティリティ型を組み合わせて複雑な実世界の型変換を行う方法を学びます。

Advanced Patterns

詳細な説明

ユーティリティ型の合成

TypeScriptのユーティリティ型の真の力は、それらを組み合わせたときに発揮されます。各ユーティリティ型は単一の変換を行います。それらを合成することで、複雑な型要件を簡潔に表現できます。

パターン: 必須-オプショナルの分割

interface User {
  id: string;
  name: string;
  email: string;
  avatar?: string;
  bio?: string;
}

// idだけが必須、それ以外はすべてオプショナル
type UserUpdate = Required<Pick<User, "id">> & Partial<Omit<User, "id">>;

パターン: イミュータブルサブセット

type ReadonlyProfile = Readonly<Pick<User, "id" | "name" | "email">>;
// {
//   readonly id: string;
//   readonly name: string;
//   readonly email: string;
// }

パターン: Deep RequiredでNull除外

type StrictUser = Required<{
  [K in keyof User]: NonNullable<User[K]>;
}>;

パターン: 判別共用体アクション

type Action =
  | { type: "SET_USER"; payload: User }
  | { type: "SET_LOADING"; payload: boolean }
  | { type: "SET_ERROR"; payload: string };

type ActionPayload<T extends Action["type"]> =
  Extract<Action, { type: T }>["payload"];

type UserPayload = ActionPayload<"SET_USER">;
// 結果: User

パターン: APIレスポンスラッパー

type ApiResponse<T> = {
  data: T;
  status: number;
  headers: Record<string, string>;
};

type PaginatedResponse<T> = ApiResponse<T[]> & {
  total: number;
  page: number;
  pageSize: number;
};

type UserListResponse = PaginatedResponse<
  Pick<User, "id" | "name" | "avatar">
>;

パターン: フォーム状態

type FormState<T> = {
  values: Partial<T>;
  errors: Partial<Record<keyof T, string>>;
  touched: Partial<Record<keyof T, boolean>>;
  isValid: boolean;
};

type UserForm = FormState<Omit<User, "id">>;

ユースケース

単一のユーティリティ型では表現できない複雑な型変換が必要な場合にユーティリティ型を合成します。フォーム状態管理、APIレイヤーの型付け、Reduxアクションパターン、柔軟なジェネリック抽象化の構築で一般的です。

試してみる — TypeScript Utility Types

フルツールを開く