ChatGPT解决这个技术问题 Extra ChatGPT

TypeScript and React - children type?

I have a very simple functional component as follows:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


const aux = (props: AuxProps) => props.children;

export default aux;

And another component:

import * as React from "react";

export interface LayoutProps  { 
   children: React.ReactNode
}

const layout = (props: LayoutProps) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {props.children}
        </main>
    <Aux/>
);

export default layout;

I keep on getting the following error:

[ts] JSX element type 'ReactNode' is not a constructor function for JSX elements. Type 'undefined' is not assignable to type 'ElementClass'. [2605]

How do I type this correctly?

To avoid reinventing wheel, you can alternatively use React.FC to define your Functional Component. i e., const layout React.FC = (props) => {/*component body*/}
you can use const XyzComponent = ({ title, children }: React.PropsWithChildren<XyzComponentPropsType> => {}`

R
Ridd

Just children: React.ReactNode.


This is the correct answer here ! JSX.Element is not good enough since a valid React children could be a string, a boolean, null... ReactChild is incomplete too for the same reasons
@PierreFerry The issue is, almost anything can be assigned to ReactNode. It doesn't help in terms of type safety, similar to typing children as any - see my answer for an explanation.
or PropsWithChildren
Confused...isn't that what OP already has?
@JanacMeena it's funny but people were finding this question when didn't know what type they should add to the children's element. It's simply just one of the first topics on Google. So if you don't know what TS type add to the element you probably will google something like "typescript react children", find this topic and the first answer, even if the question is quite about something different :)
K
Karol Majewski

In order to use <Aux> in your JSX, it needs to be a function that returns ReactElement<any> | null. That's the definition of a function component.

However, it's currently defined as a function that returns React.ReactNode, which is a much wider type. As React typings say:

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

Make sure the unwanted types are neutralized by wrapping the returned value into React Fragment (<></>):

const aux: React.FC<AuxProps> = props =>
  <>{props.children}</>;

I don't understand why it's needed to wrap children in a React Fragment. Care to explain? In React it's perfectly valid to just return children;.
Not if children are an array. If that's the case you need to either wrap them in a single node or use React Fragments.
Yeah, in my code I have: return React.Children.count(children) > 1 ? <>{children}</> : children, but typescript complains about that. I replaced it with just <>{children}</>.
It complains because children is a list of elements that happens to contain only 1 item. I think it would work if you did return React.Children.count(children) > 1 ? <>{children}</> : children[0] @sanfilippopablo
This doesn't appear to be working in newer versions of React. I console logged and can confirm that I have a children prop, but even with <React.Fragment> surrounding {props.children} I am not returning anything.
W
Wilk

You can use ReactChildren and ReactChild:

import React, { ReactChildren, ReactChild } from 'react';
 
interface AuxProps {
  children: ReactChild | ReactChildren;
}

const Aux = ({ children }: AuxProps) => (<div>{children}</div>);

export default Aux;

If you need to pass flat arrays of elements:

interface AuxProps {
  children: ReactChild | ReactChild[] | ReactChildren | ReactChildren[];
}

For anyone who ended up here, this is wrong. ReactChildren does not equal to ReactChild[] since it is type for utility function. reactjs.org/docs/react-api.html#reactchildren. if you use ReactChild | ReactChildren , you won't be able to pass children as an array.
@kay Thanks for that. I added the flat arrays definition to the answer. It should cover your case too. Let me know.
thank you! However, I don't think ReactChildren is the right type here. I think children: ReactChild | ReactChild[] is enough or just ReactNode
I disagree. Its better to create a type/interface as ReactNode compared to ReactChild and ReactChildren since it accepts more than just ReactChild/ReactChildren. Or even better, use PropsWithChildren as others have mentioned.
s
sunknudsen

This is what worked for me:

interface Props {
  children: JSX.Element[] | JSX.Element
}

Edit I would recommend using children: React.ReactNode instead now.


That's not broad enough of a definition. The child could be a string.
At least this example worked for me since my component was supposed to expect 1 or more JSX.Elements. Thanks!
This will not work with JavaScript expressions as the children <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Using children: ReactNode will work.
or use PropsWithChildren as others have mentioned. I personally prefer it to children: React.ReactNode
S
Sibren

You can also use React.PropsWithChildren<P>.

type ComponentWithChildProps = React.PropsWithChildren<{example?: string}>;

This is exactly what I've been doing and I think it's the cleaner way to use the right typing.
I like this solution because it is extensible. type AuthProviderProps = React.PropsWithChildren<{}> allows me to encapsulate React types, and extend with my own when needed.
cleanest way, the one i was looking for
This feels the cleanest because it's using React's own published types so if the definition of what constitutes a react child changes in future versions of React, it is reasonable to assume that this type will also be updated. Solutions based on ReactChild | ReactChildren, would probably need to be updated in that case.
G
Gapur Kassym

A React Node is one of the following types:

Boolean (which is ignored)

null or undefined (which is ignored)

Number

String

A React element (result of JSX)

An array of any of the above, possibly a nested one


j
jsina

you can declare your component like this:

const MyComponent: React.FunctionComponent = (props) => {
    return props.children;
}

the general consensus today is that React.FunctionComponent (or the shorthand React.FC) is discouraged. In your case, I would remove React.FC usage and just do const MyComponent = ({children}: PropsWithChildren<{}>)
C
Community

The function component return type is limited to JSXElement | null in TypeScript. This is a current type limitation, pure React allows more return types.

Minimal demonstration snippet

You can either use a type assertion or Fragments as workaround:

const Aux = (props: AuxProps) => <>props.children</>; 
const Aux2 = (props: AuxProps) => props.children as ReactElement; 

ReactNode

children: React.ReactNode might be suboptimal, if the goal is to have strong types for Aux.

Almost anything can be assigned to current ReactNode type, which is equivalent to {} | undefined | null. A safer type for your case could be:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Example:

Given Aux needs React elements as children, we accidently added a string to it. Then above solution would error in contrast to ReactNode - take a look at the linked playgrounds.

Typed children are also useful for non-JSX props, like a Render Prop callback.


P
Penny Liu

I'm using the following

type Props = { children: React.ReactNode };

const MyComponent: React.FC<Props> = ({children}) => {
  return (
    <div>
      { children }
    </div>
  );

export default MyComponent;

the general consensus today is that React.FunctionComponent (or the shorthand React.FC) is discouraged. In your case, I would remove React.FC usage and just do const MyComponent = ({children}: PropsWithChildren<{}>)
propsWithChildren<{}> however also introduces some codesmells with {} being any
If you are using React.FC, there is no need to include children type, as it is already defined on the base FC typings.
R
Ravi Makwana
import { ReactNode, FC } from 'react'

type Props = { children: ReactNode }

const App: FC<Props> = ({children}) => (<div>{children}</div>)

FC already has children, so no need to redeclare it
N
Nickofthyme

The general way to find any type is by example. The beauty of typescript is that you have access to all types, so long as you have the correct @types/ files.

To answer this myself I just thought of a component react uses that has the children prop. The first thing that came to mind? How about a <div />?

All you need to do is open vscode and create a new .tsx file in a react project with @types/react.

import React from 'react';

export default () => (
  <div children={'test'} />
);

Hovering over the children prop shows you the type. And what do you know -- Its type is ReactNode (no need for ReactNode[]).

https://i.stack.imgur.com/ffpa8.png

Then if you click into the type definition it brings you straight to the definition of children coming from DOMAttributes interface.

// node_modules/@types/react/index.d.ts
interface DOMAttributes<T> {
  children?: ReactNode;
  ...
}

Note: This process should be used to find any unknown type! All of them are there just waiting for you to find them :)


Control + click (on windows) takes you to said type-definition. May be common knowledge but I had to look up. Thanks for teaching us to fish instead of just giving fish btw.
M
Mike S

From the TypeScript site: https://github.com/Microsoft/TypeScript/issues/6471

The recommended practice is to write the props type as {children?: any}

That worked for me. The child node can be many different things, so explicit typing can miss cases.

There's a longer discussion on the followup issue here: https://github.com/Microsoft/TypeScript/issues/13618, but the any approach still works.


f
francis

You can also use JSX.ElementChildrenAttribute

export default function Layout({children}: JSX.ElementChildrenAttribute) {
    return <div>
        {children}
    </div>
}

T
Tim Iles

These answers appear to be outdated - React now has a built in type PropsWithChildren<{}>. It is defined similarly to some of the correct answers on this page:

type PropsWithChildren<P> = P & { children?: ReactNode };


What is P in the case of that type?
P is the type of your component's props
Yes! this worked nicely. interface LayoutProps {} const Layout = ({children}: PropsWithChildren<LayoutProps>) => {...}
M
Mr.Ghamkhar

This has always worked for me:

type Props = {
  children: JSX.Element;
};

I would recommend just using React.PropsWithChildren<{}> or at least React.ReactNode instead of JSX.Element since it accpets more than just Element type
This work for me too. I think typescript typings is becoming messy now.
D
Denis

As a type that contains children, I'm using:

type ChildrenContainer = Pick<JSX.IntrinsicElements["div"], "children">

This children container type is generic enough to support all the different cases and also aligned with the ReactJS API.

So, for your example it would be something like:

const layout = ({ children }: ChildrenContainer) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {children}
        </main>
    <Aux/>
)

C
CodingYourLife

For me @Sibren's answer was not clear enough but I found this SO anwer and made it all inline (that's maybe not the shortest way but the one I find the easiest to grasp).

function MyComponentWithChildren({
    customProp,
    children, /*notice the children are implicit*/
}: React.PropsWithChildren<{ customProp: any }>) {
    return <div>{children}</div>;
}

ع
عبد الحميد مرعي

you should know that any react component should return null or React.Element, but the type of props.children is React.ReactNode, so you need to use the props.children inside an Element to make the babel configure the constructor of the Element. the second rule of any react component is that the first letter of the naming should be a capital letter to let the react recognize that the component isn't a html tag.

so the code should be like this.

const Aux = (props: AuxProps) => <>props.children</>;

another hint if you still using typescript, the functional component should be type of React.FC like this

type Props = {
   title: string;
}

const Aux:React.FC<Props> = (props) =>
(
    <div>
        <h3>{props.title}</h3>
        { props.children }
        {/* children is exist by default in type React.FC */}
    </div>
)

C
Champignon

this solution works perfectly fine for me

interface Props {
    children: Array<ReactElement<ChildProps, JSXElementConstructor<ChildType>>>;
}

update: a comprehensive example so that it is easier to understand.

interface ChildProps {}

class ChildComponent extends React.Component<ChildProps> {}

interface ParentProps {
    children: Array<ReactElement<ChildProps, JSXElementConstructor<ChildComponent>>>;
}

class ParentComponent extends React.Component<ParentProps> {}

If your "children" should be safely typed, within your parent component, this the one and only solution. Thank you very much, Sir!
K
Kivylius

You can create a simple component that outputs just children prop without type or interface with FC (functional component). You have to wrap with empty jsx tags <>, as children can be undefined or null:

import { FC } from "react";

export const Layout: FC = (props) => {
  return <>{props.children}</>;
};

-- or --

import { FC } from "react";

export const Layout: FC = ({ children }) => <>{children}</>;

I
Ishan Fernando

You can also extends React.PropsWithChildren to your interface which contains children property.

interface Props extends React.PropsWithChildren{
    listItems: Items[],
    clickItem?: () => void,
    
}

Or you can directly define the children

interface Props{
    listItems: Items[],
    clickItem?: () => void,
    children: React.ReactNode  
}

const List:FC<Props> = ({listItems,clickItem,children}) =>  {

  return (
    <>
    {children}
    </>
    
  )
}

Or you can do like this. This is a another way to defining prop type

const List = ({ children }: {children: React.ReactNode}) =>  {

D
Dinesh Pandiyan

React components should have a single wrapper node or return an array of nodes.

Your <Aux>...</Aux> component has two nodes div and main.

Try to wrap your children in a div in Aux component.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

const aux = (props: AuxProps) => (<div>{props.children}</div>);

export default aux;

Not true. In React 16+ this is no longer the case, plus the error would say this if it were the case