The short answer is that DetailedHTMLProps<E, T>
only extends HTMLAttributes<T>
but React.HTMLProps<T>
extends AllHTMLAttributes<T>
, which then itself extends HTMLAttributes<T>
.
Use React.ComponentWithRef<"tag-here">
or React.ComponentWithoutRef<"tag-here">
when you want to have the narrowest set of HTML props for your component. Use React.HTMLProps<HTMLElement>
when you want the widest.
What ends up happening is when you pass HTMLDivElement
into the generic type parameter of HTMLProps<T>
, you're first taking a stop at AllHTMLAttributes<T>
before going through the inheritance hierarchy down through HTMLAttributes<T>
, and eventually ending at DOMAttributes<T>
. This gives you the kitchen sink of all HTML attributes, which makes sense. The only thing that the generic parameter HTMLDivElement
is doing here is being drilled down into a ClassAttributes<T>
interface that grabs the LegacyRef<T>
for your component and also into the eventual DOMAttributes<T>
interface that uses T
to correctly type all of the event handlers like onClick
, onDrag
, onBlur
, and the many others. Passing HTMLDivElement
into HTMLProps<T>
will do virtually nothing to narrow the type of props that are returned.
When you use DetailedHTMLProps<E extends HTMLAttributes<T>, T>
however, based on what E
is passed in, you will access the narrowest relevant interface. Note that when you choose the <a>
tag, you're accessing a uniquely different interface than when you choose the <div>
or <summary>
tag, which both share React.HTMLAttributes<T>
interface DetailedHTMLProps<E extends HTMLAttributes<T>, T> = E & ClassAttributes<T>;
interface JSX.IntrinsicElements {
a: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
// ... some distance down
div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
// ... some distance down
summary: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
// ...
}
As of React 17.0 (when I'm writing this), the calling code should use ComponentPropsWithRef<T extends ElementType>
or ComponentPropsWithoutRef<T extends ElementType>
and typescript should get the detailed props for you based on the type parameter passed in.
export type MyType = React.ComponentPropsWithoutRef<"div">;
// if we follow the type magic to its conclusion
React.ComponentPropsWithoutRef<"div"> = PropsWithoutRef<ComponentProps<"div">>;
ComponentProps<"div"> = JSX.IntrinsicElements["div"]
JSX.IntrinsicElements["div"] = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;