How to Type HTML Elements
On a recent project, I had a link that I wanted to turn into a styled component that included an icon afterwards.
The problem with the first attempt was obvious. I wasn’t typing my React component at all:
const JumpDownLink: React.FC = ({children, ...props}) => {
return (
<JumpDownLinkAnchor {...props}>
{children}
<JumpDownIcon />
</JumpDownLinkAnchor>
)
}
Thus, attempting to use my component and pass in any of the a
attributes, such
as href
, resulted in the error
TS2322: Type '{ children: string; href: string; }' is not assignable to type
'IntrinsicAttributes & { children?: ReactNode; }'. Property 'href' does not
exist on type 'IntrinsicAttributes & { children?: ReactNode; }'.
To fix that, I needed to indicate type my component so that any standard a
attribute could be set as a prop.
This can actually be done quite easily using React.HTMLProps
. That got me
closer to a working solution:
const JumpDownLink: React.FC<React.HTMLProps<HTMLAnchorElement>>
= ({children, ...props}) => {
return (
<JumpDownLinkAnchor {...props}>
{children}
<JumpDownIcon />
</JumpDownLinkAnchor>
)
}
However, because JumpDownLinkAnchor
was a styled component using EmotionJS, I
ran into another error: Types of property 'as' are incompatible.
.
React.HTMLProps
extends AllHTMLAttributes
, which includes a long list of
possible HTML properties, including as
, which is defined as a string. Emotion’s
CreateStyledComponent
also uses an as
property but its as
needs a React element type.
Since as
isn’t a valid attribute on an a
element anyway, the solution was to
use Typescript’s Omit to remove as
from the list of props allowed on the link element.
Solution
const JumpDownLink: React.FC<Omit<
React.HTMLProps<HTMLAnchorElement>,
'as'
>> = ({ children, ...props }) => {
return (
<JumpDownLinkAnchor {...props}>
{children}
<JumpDownIcon />
</JumpDownLinkAnchor>
);
};