Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

ComponentPropsWithoutRef<'button'> と ButtonHTMLAttributes<HTMLButtonElement> は何が違うのか?

A. 型定義的には同じ *1。HTML要素向けのComponentPropsの実装でXxxHTMLAttributesが使われる、という関係。

結論としては同じものとみなして良いんですが、じゃあどう使い分ければ良いのか? という話ですよね。この話をするにはそもそもComponentPropsが何者なのかを知る必要があります。

ComponentPropsは コンポーネントのプロパティを取得するユーティリティ型

ComponentPropsは、渡されたコンポーネントまたはHTML要素のプロパティを取得するユーティリティ型 です。こんな感じになります↓

const MyComponent = (props: MyProps) => <div />;

type MyComponentProps = ComponentProps<typeof MyComponent>; // MyComponentのプロパティ(MyProps)が得られる
type ButtonElementProps = ComponentProps<'button'>; // button要素に設定できる属性が取得できる

ButtonHTMLAttributesとの違い

ButtonHTMLAttributes も button 要素の属性を表現しています。というか、ComponentProps<'button'> の内部実装はほぼ ButtonHTMLAttributes そのものです。

これは ComponentProps の実装を追いかけるとわかります。まず、 ComponentProps<'button'>JSX.IntrinsicElements['button'] です。

    type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> = T extends
        JSXElementConstructor<infer Props> ? Props
        : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T]
        : {};

該当コード

JSX.IntrinsicElements['button']DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> です。

        interface IntrinsicElements {
            // ...
            button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;

該当コード

DetailedHTMLPropsはrefを追加するためのものなので、ほぼ ButtonHTMLAttributes<HTMLButtonElement>ComponentProps<'button'> の実体です。

結論: ComponentProps を使うのが良さげ

このように、実装としてはどちらを使っても同じ効果が得られます。ただし、結論としては、「コンポーネント(HTML要素)のプロパティ(属性)を扱いたいシーンであればComponentPropsを使う」のが良いと思います。ButtonHTMLAttributesを直接使う代わりにComponentPropsを使えば、「button要素のプロパティを扱いたいんだな」とコードの意図が明確に表現できるので。

*1:正確に言えば、ButtonHTMLAttributes にrefが追加されてからrefをOmitしたものがComponentPropsWithoutRef。なので完全に一致しているわけではないが、ユースケース上は全く同じとみなして良いはず