I was working on a component that takes in a children prop that takes in an img
element + another prop. But when I go on to use the component and pass all the props, I receive the following error:
Type 'Element' has no properties in common with type 'Pick<ImgHTMLAttributes, "src" | "alt">'.ts(2559)
App.tsx(5, 3): The expected type comes from property 'children' which is declared here on type 'IntrinsicAttributes & Props'
Here is an example of my code (Link to CodeSandbox):
type Props = {
children: Pick<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt">
otherProp?: any
}
const Image = ({children, otherProp}: Props) => {
return (
<picture>
...
{children}
</picture>
)
}
I honestly don't understand what IntrinsicAttributes
is, I can see that it is extending my Prop
with this other type IntrinsicAttributes
but not sure why exactly. And also the errors seem to go away when I remove otherProp
. An explanation would be really helpful.
Thanks a lot.
The type ImgHTTMLAttributes<HTMLImageElement>
is a type that describes a simple object, that contains all properties that img
jsx element can accept.
Pick<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt">
is just a type { src: string; alt: string }
. Note, that it does not describe the actual img
jsx element, it just describes props that it potentially has.
In typescript there is no way to say "this component only accepts an img
jsx element that only has props alt
and src
" so there is no way to type this, at least not like you show in the sandbox. What you should probably do instead is get rid of children
in the first place, and just pass src
and alt
straight to the Image
component
interface Props {
src: string
alt: string
otherProp?: any;
}
const Image = ({ src, alt, otherProp }: Props) => {
return <picture><img src={src} alt={alt} /></picture>;
};
const el = <Image otherProp="" src="cool" alt="Image alt tag" />
Or if you don't want to write type definitions for src
and alt
yourself, you could take the existing types from ImgHTTMLAttributes
like you did in your example, but it should look like this:
type Props = Pick<ImgHTMLAttributes<HTMLImageElement>, "src" | "alt"> & {
otherProp?: any;
}
const Image = ({ src, alt, otherProp }: Props) => {
return <picture><img src={src} alt={alt} /></picture>;
};
const el = <Image otherProp="" src="cool" alt="Image alt tag" />
Speaking about IntrinsicAttributes
, this is a type that describes props that all components can receive, even if you didn't provide them in your props definitions. They are usually used internally by the library, this is why they are called intrinsic. For example, if you are using react, the only intrinsic property is key
: you can attach it to any component without defining its types anywhere, and it is used internally by react. When you use any jsx component, its prop types are descibed by the type IntrinsicAttributes & PropsThathYouDefinedForYourComponent
, in your case, IntrinsicAttributes & Props
. This way, when you use this jsx component, you can pass both your custom props and internal library props, and the type checking still works