【テクニカル・上級編】 PickやOmitを用いたPropsの型操作 – React実践ガイド

型安全のその先へ:PickとOmitで切り拓く「疎結合なコンポーネント」の極意

Reactで中規模以上のアプリケーションを構築していると、必ずぶつかる壁がある。それは「Propsの肥大化」と「型定義の硬直化」だ。

「このコンポーネント、本当は特定の3つのPropsしか使わないのに、親から渡された大きなオブジェクトを丸ごと受け取っている」
「APIレスポンスの型をそのままPropsに流し込んだせいで、バックエンドの変更がフロントエンドのいたるところでコンパイルエラーを引き起こす」

現場のリアルな地獄は、こうした「過剰な依存」から始まる。今日は、TypeScriptの `Pick` と `Omit` を駆使し、型レベルでコンポーネントをデカップリング(疎結合化)させる、シニアレベルの設計術を語ろう。

—

1. 「Propsの横流し」というアンチパターン

多くのジュニアエンジニアは、とりあえず `interface Props { user: User }` のように、ドメインモデルをそのままPropsに突っ込む。だが、これはアーキテクチャ上の爆弾だ。

このコンポーネントは `User` 型の構造を完全に知ってしまっている。もし `User` 型が変更されたら? もし将来的に `User` オブジェクトの一部しか必要ないコンポーネントが増えたら? そのたびに不要な再レンダリングや、不要なメモリ消費(大きなオブジェクトの参照渡し)が発生する。

ここで `Pick` の出番だ。

// ドメインの巨大な型定義
interface User {
id: string;
name: string;
email: string;
bio: string;
avatarUrl: string;
lastLoginAt: string;
}

// 必要なものだけを抽出する(Pick)
// これにより、User型の構造が変わっても、このコンポーネントは影響を受けない
type UserProfileProps = Pick;

const UserProfile = ({ name, avatarUrl, bio }: UserProfileProps) => {
return (

);
};

`Pick` を使うと、コンポーネントは「自分が本当に必要なインターフェース」のみを定義できる。これは単なるコードの整理ではない。「どのデータがコンポーネントのレンダリングに直結しているか」を型レベルで明示することで、Reactの最適化(`React.memo`)の精度が劇的に向上する。

—

2. Omitによる「Propsの継承」と競合回避

次に重要なのが `Omit` だ。特に、既存のHTML要素を拡張するような「ラッパーコンポーネント」を作る際に猛威を振るう。

よくあるミスが、`HTMLAttributes` をそのまま継承して、特定のPropsを上書きしようとして型矛盾を起こすケースだ。

// ボタンコンポーネントの例
// HTMLのbutton要素から ‘onClick’ を一度除外(Omit)して、独自に定義し直す
interface CustomButtonProps extends Omit, ‘onClick’> {
onClick: (event: React.MouseEvent) => Promise; // 非同期関数として再定義
}

const CustomButton = ({ onClick, …props }: CustomButtonProps) => {
const handleClick = async (e: React.MouseEvent) => {
// 非同期処理中の競合を防ぐガード句の挿入
await onClick(e);
};

return

コメント

タイトルとURLをコピーしました