😎

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