简体   繁体   中英

In Typescript, how can I tell which React functional component a variable is?

I have a CustomIcon component that I use sometimes for my icons, and other times I use MUI's icons. I have a type with a variable defined as:

type ParentComponentProps = {
  icon: React.ReactElement<SvgIconProps> | typeof CustomIcon;
};

This way, I can call ParentComponent either this way:

<ParentComponent icon={<HomeIcon />}/>

or

<ParentComponent icon={<CustomIcon {...args}/>}/>

I want a way to determine from the value of icon if it's using the MUI icon or my CustomIcon . Within ParentComponent , I want to add arguments to the icon that gets passed in so that the consumers won't have to (eg I want to add className classes in addition to what the consumer might specify).

I've tried various ways to approach this, using typeof , instanceof , and ReturnType but they're all generally limited due to needing a value for the comparison rather than a type.

Is there a way to solve this using a condition, or does it need to be solved another way?

the type of created react component (in the format of <Icon /> ) is always be JSX.Element , if you need to some type checks, you should pass the component itself into the props

so the component props will be like belows, note that the name is changes to Icon because if you do <icon /> inside, it will be consider you are try to use a native element call icon :

type ParentComponentProps = {
  Icon: React.ComponentType<SvgIconProps> | typeof CustomIcon;
};

but usually I will let the props of CustomIcon (assume it is called CustomIconProps ) compatible with SvgIconProps , and the types will be:

type CustomIconProps = SvgIconProps & {
// ...
};

type ParentComponentProps = {
  Icon: React.ComponentType<SvgIconProps>;
};

which will be more clear.

and the usage:

<ParentComponent Icon={HomeIcon}/>
<ParentComponent Icon={CustomIcon}/>

if you need to pass args to CustomIcon :

<ParentComponent Icon={(props: CustomIconProps) => <CustomIcon {...props} {...args}/>}/>

I want a way to determine from the value of icon

Where do you want to use that?

If you need it to determine another type, for example to type an style prop, you can do something like this:

      type SvgIcon = React.ReactElement<SvgIconProps>

      type ParentComponentProps<Icon extends  SvgIcon | typeof CustomIcon> = {
               icon: Icon;
               style: Icon extends SvgIcon ? Type1 : Type2
        
       };

Even when you do:

<ParentComponent
  icon={<CustomIcon {...args}/>}
/>

...you are actually passing a ReactElement to the icon prop.

More generally, this is similar to the children prop, or any slot content prop. We generally type them as ReactNode , which also takes a string , null , or an array of ReactNode .

It is indeed possible to tell from a ReactElement from which Component it was built, typically by looking into its type.name property, but you need to guard it first, as described eg in Accessing child.type.name in React Typescript

function ParentComponent({
    icon
}: {
    icon: React.ReactElement
}) {
    if (React.isValidElement(icon) && typeof icon.type === "function") {
        icon.type.name // Okay
    }
    return <></>
}

Playground Link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM