I've got a React component <Wrapper>
that I can use as follows:
// Assuming Link is a component that takes a `to` prop: <Link to="/somewhere">label</Link>
<Wrapped as={Link} to="/somewhere">label</Wrapped>
If no as
prop is passed, it will assume an <a>
. But if a component is passed to as
, all props that are valid for that component should now also be valid props of Wrapped
.
Is there a way to type this in TypeScript? I was currently thinking along these lines:
type Props<El extends JSX.Element = React.ReactHTMLElement<HTMLAnchorElement>> = { as: El } & React.ComponentProps<El>;
const Wrapped: React.FC<Props> = (props) => /* ... */;
However, I'm not sure whether JSX.Element
and/or React.ComponentProps
are the relevant types here, and this does not compile because El
can not be passed to ComponentProps
. What would the correct types there be, and is something like this even possible?
The tyes you need are ComponentType
and ElementType
.
import React, { ComponentType, ElementType, ReactNode } from 'react';
type WrappedProps <P = {}> = {
as?: ComponentType<P> | ElementType
} & P
function Wrapped<P = {}>({ as: Component = 'a', ...props }: WrappedProps<P>) {
return (
<Component {...props} />
);
}
With that, you are able to do:
interface LinkProps {
to: string,
children: ReactNode,
}
function Link({ to, children }: LinkProps) {
return (
<a href={to}>{children}</a>
);
}
function App() {
return (
<div>
<Wrapped<LinkProps> as={Link} to="/foo">Something</Wrapped>
<Wrapped as="div" style={{ margin: 10 }} />
<Wrapped />
</div>
);
}