1
votes

I have a component that can accept React children as either a node or an array of nodes. I want to be able to detect if children is an array of nodes, but I am getting the following Typescript error:

TS2339: Property 'length' does not exist on type 'string | number | true | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any> | null) | (new (props: any) => Component<any, any, any>)> | ... 47 more ... | (ReactNode[] & ReactPortal)'.   Property 'length' does not exist on type 'number'.

Is there a way in Typescript where I detect if children has length? Thanks.

import React from 'react';

interface Props {
  children: React.ReactNode | React.ReactNode[];
}

const SampleComponent: React.FC<Props> = ({ children }) => {
  if (children && children.length) {
    return children.map((child) => (
      <div>{child}</div>
    ));
  }

  return children;
};

export default SampleComponent;

enter image description here

3
You might want to avoid React.FC, here's a link to a MR github.com/facebook/create-react-app/pull/8177slaid3r

3 Answers

3
votes

There is a React.Children API for dealing with the props.children (which is an opaque data structure).

Also, React.Children has methods like count, map etc.

if (React.Children.count(children)) {
  return React.Children.map(children, (child) => <div>{ ..todo.. }</div>)
}

It has TypeScript support too. So, you won't need to cast the types.

3
votes

[Update]

Please check out Ajeet Shah's response with using React.Children API.

[Original]

Cast children to React.ReactNode[] before checking length like this:

(children as React.ReactNode[]).length

You could also create a type predicate:

https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates

1
votes

The best way in this case is not use Reacf.FC

Defined the props like const MyButton = (props: MyProps)=>... Or use React.VFC

Function Components - Why is React.FC discouraged?

enter image description here