React buttonの汎用的な型定義
React+Typescriptで開発していると、必ずPropsの型定義をしますよね。
ただ、後になって型の拡張性に悩むなんてことがよくあると思います。
そんな悩みを解消する方法を見つけたので紹介します。
前提
見つけた後(生成AI様のおかげ)にネットで調べると、すでに色々な方が紹介していました。
ただ、私的に目から鱗な内容だったので学習メモとして残します。
結論
まず先に結論。「HTML要素の型」を拡張して使いましょう👍
例えばButtonコンポーネントであれば、
React.ButtonHTMLAttributes<HTMLButtonElement>
を使います。
実際に何が解消されたのか、経緯とともに説明します。
経緯
🤓「よし、Buttonコンポーネント作るぞ!まずは、<button>を用意して、tailwindでいい感じにスタイル当てて、押した時の処理を引数で受け取って...こんな感じや!」
type ButtonProps = {
children: React.ReactNode;
className?: string;
onClick: () => void;
}
const Button = (props: ButtonProps) => {
return (
<button
className={`iikanji ${props.className}`}
onClick={props.onClick}
>
{props.children}
</button>
);
};
この時点ではこれで特に問題はありませんでした。
少し時間が経って、ちょっと機能を拡張したButtonを作りたくなりました。
🤓「Buttonを拡張するから、このButtonPropsを拡張したExtendButtonPropsを作るか!ただ、このExtendButtonはonClickは必須じゃないからな...そもそもHTMLのbuttonってonClick必須じゃないし、このコンポーネントのButtonPropsのonClickはオプショナルにするか!」
type ButtonProps = {
children: React.ReactNode;
className?: string;
onClick?: () => void; // 修正
}
type ExtendButtonProps = ButtonProps & {
extendArg: string;
}
こんな感じで実装をしていました。でもこれってHTMLのbutton(以降buttonとします)でできることをそのまま引数にしてるだけです。
置き換える
ButtonPropsはbutton本来のpropsをただ別名の型にしているだけです。ということは別にbutton本来のPropsの型が使えればいいわけですね。
type ExtendButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
extendArg: string;
}
もし拡張したくなったり、どうしてもこのパラメータは除外したいなどがあれば、&で結合したり、Omitで特定のパラメータを除外すれば良さそうです。
さいごに
buttonに限らず、他のタグでも同じ考えが使えます。
Discussion