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>
);
};