Search code examples
reactjstypescriptnpmchildren

Only allow certain component as children (React, TypeScript)


This question is asked multiple times and I went through answers for hours. However, I am unable to get my code to work. Here is the problem.

I have a Steps component that is a wrapper. And I have multiple Step components that goes into Steps component as children.

In my Steps component, I want to create a new children array that only consists of Step components. I simply want to ignore everything else. Meanwhile showing some warning on the console that anything other than Step component child will be disregarded, would be fine but whatever, that is not important at this point.

<Steps>
  <Step>Hello</Step> // should be rendered
  <div>Please ignore me</div> // should be ignored
  <Step>How are you?</Step> // should be rendered
</Steps>

The problem is, I am working with TypeScript and I cannot access to child.type as many other answers in other questions suggest.

enter image description here

Error is as follows:

Property 'type' does not exist on type 'ReactChild | ReactFragment | ReactPortal'.
  Property 'type' does not exist on type 'string'.ts(2339)

When I hover over child, it shows this:

(parameter) child: React.ReactChild | React.ReactFragment | React.ReactPortal

Could you point me to the right direction please? How can I check the type of child and return a new children array with only those that is instance of Step?


Solution

  • Finally found the solution.

    When I apply React.Children.Map on props.children, child is expected to be of type string | number | boolean | {} | React.ReactElement<any, string | React.JSXElementConstructor<any>> | React.ReactNodeArray | React.ReactPortal.

    After applying React.isValidElement(child) filter on child, it removes all the non-ReactElement items from the union type and leaves React.ReactElement<any, string | React.JSXElementConstructor<any>> | React.ReactPortal behind. After that, type property appears on the child value.