Reactの型定義で消耗しないために:`ComponentProps`を使いこなすプロの流儀
現場でReactを書いていると、一度はこんな経験をするはずだ。「既存のボタンコンポーネントに機能を追加したいが、Propsの型定義をコピペして管理するのが面倒だ」「親から渡されたコンポーネントのPropsをそのままラップしたいのに、型が微妙に噛み合わない」。
TypeScript×Reactの現場において、「型をDRY(Don’t Repeat Yourself)に保つ」ことは、コードの保守性を左右する生命線だ。今日は、React標準の強力なユーティリティ型である `React.ComponentProps
—
1. なぜ `ComponentProps` なのか?
結論から言うと、手動で型を再定義するのは「負債の種」でしかない。
例えば、`Button`コンポーネントのPropsをいちいちインターフェースで書き出していると、オリジナルのPropsが変更された瞬間に、ラップした側のコンポーネントで型エラーの嵐が吹き荒れることになる。
`React.ComponentProps
—
2. 実務で直撃する「コンポーネント・ラッピング」の現場
最もよくあるケースは、既存のUIコンポーネントをラップして、独自のロジックを追加したいときだ。
import React from ‘react’;
// 既存の汎用ボタンコンポーネント(ライブラリなど)
const BaseButton = ({ variant, …props }: { variant: ‘primary’ | ‘secondary’ } & React.ButtonHTMLAttributes
return ;
};
// 【ここがプロの技】BaseButtonのPropsを抽出して拡張する
// ComponentPropsを使えば、HTML属性からBaseButton独自のvariantまで全て追従できる
type MyButtonProps = React.ComponentProps
isLoading?: boolean;
};
export const MyButton = ({ isLoading, …props }: MyButtonProps) => {
return (
{isLoading ? ‘Loading…’ : props.children}
);
};
なぜこれが美しいのか?
1. 変更に強い: `BaseButton` のPropsが将来増えても、`MyButton` 側の型定義を一行も書き換える必要がない。
2. 安全性が高い: `HTMLButtonElement` の型(`onClick`, `aria-label` 等)も全て継承されるため、IDEの補完が完璧に効く。
3. ノイズが少ない: 型定義ファイルが数百行に及ぶような惨事を防げる。
—
3. 現場で使える「型抽出」のもう一つの引き出し
`ComponentProps` は素晴らしいが、一つだけ注意点がある。それは「コンポーネントが関数型として適切に定義されているか」だ。
もし複雑なコンポーネントで、Propsの型がエクスポートされていない場合、`ComponentProps` はあなたの最後の砦になる。逆に、コンポーネント側でPropsの型をちゃんと `export` しておくのも、チーム開発における礼儀だ。
以下に、現場でよくやる「特定のPropsだけ除外してラップする」高等テクニックを置いておく。
import React from ‘react’;
// 例えば、BaseButtonから’variant’だけを拝借して、別物を作りたい場合
type BaseProps = React.ComponentProps
// Omitを使って特定のPropsを間引く。これができるとアーキテクトとしては一人前だ
type SimpleButtonProps = Omit
export const SimpleButton = (props: SimpleButtonProps) => {
// variantは受け付けない純粋なボタンとして振る舞う
return ;
};
—
4. 裏側で何が起きているのか?
少しだけ深掘りしよう。ブラウザが実行するJSには「型」など存在しない。しかし、TypeScriptの `typeof` は、そのコンポーネントが関数(またはクラス)であることを解析し、その引数の型定義(つまり第一引数のProps)を抽出している。
Reactの内部実装では、`ComponentProps` は単なるマッピングではない。`React.ComponentType
` からPropsの型 `P` を抽出する複雑な条件付き型(Conditional Types)の組み合わせだ。君たちがこれを使うことで、コンパイル時にReactのProps伝播ルールを強制できる。これは、バグを「書く前に消す」ための防御壁なのだ。
—
5. シニアからのアドバイス
「何でもかんでも `ComponentProps` で型を抽出する」のは、時にコードの依存関係を複雑にする。
- ライブラリのコンポーネントを包むとき: 積極的に使え。
- 自作の小さなコンポーネント: 可能であれば、明示的にPropsの型を定義した方が、APIのデザイン(何を受け取るべきか)を意識できる。
型安全とは、単にエラーを消すことではなく、「誰が使っても直感的に正しく動くAPI」を設計することだ。`ComponentProps` はそのための強力なツールだが、過信せず、常に「そのコンポーネントの責務は何か?」を問い続けてほしい。
さあ、エディタに戻って、冗長な型定義を一行ずつ消していこう。その先に、クリーンでメンテナンス性の高いプロダクトが待っているはずだ。

コメント