I have this component, which is just a title and an icon on the right, and the container of this component can be a button, an anchor element, or a Link element from react-router-dom
.
I was able to create the container dynamically like so:
import { Link } from "react-router-dom";
// ....
type Props = {
as: "button" | "a" | "Link";
title: string;
icon: string;
};
const ActionItem: React.FC<Props> = ({ as, title, icon }: Props) => {
const Container = styled(as === "Link" ? Link : as)`
display: flex;
// ...
`;
return (
<Container>
{title}
<div style={{ flex: 1 }} />
{icon}
</Container>
);
};
But now what I'm trying to accomplish is to have dynamic props depending on the as
prop, so, for example, if the as
prop is equal to Link
show all the props for the Link component like to
, replace
, etc. Or if the as
is equal to a
, show all the props for the anchor element like href
, target
, etc.
<ActionItem
as="Link"
to="/profile"
...
/>
I tried this but it didn't work, mostly because in the example they are using native HTML elements, and I need to use those (button and anchor element) plus the Link component.
I don't know if what I'm trying to find is possible or if it is another possible solution for this?
BTW, here's a code sandbox with the code
Edit: Here's the sandbox of the code working as it should
CodePudding user response:
You should use a so-called discriminating union (or discriminative union). Let's use the as
prop as the discriminator.
You already have
type Props = {
as: "button" | "a" | "link"
}
Break that out to
type Props =
{ as: "button" } |
{ as: "a" } |
{ as: "Link" }
Now simply add to each part of the union what props you expect there for Typescript to deduce what other properties are possible, given the discriminator prop as
:
type Props =
{ as: "button" } & ButtonProps |
{ as: "a" } & AProps |
{ as: "Link" } & LinkProps
Done!
By the way, I used made up names for the complementary props you need, you have to look up the names of the types you want to intersect your discriminator prop with yourself.