Typescript react function component children

React 18 TypeScript children FC

I upgraded to React 18 and things compiled fine. Today it seems every single component that uses children is throwing an error. Property ‘children’ does not exist on type ‘IPageProps’. Before children props were automatically included in the FC interface. Now it seems I have to manually add children: ReactNode . What is the correct typescript type for react children? Is this part of the React 18 update, or is something screwed up in my env? package.json

"react": "^18.0.0", "react-dom": "^18.0.0", "next": "12.1.4", "@types/react": "18.0.0", "@types/react-dom": "18.0.0", 
< "compilerOptions": < "target": "esnext", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve", "alwaysStrict": true, "sourceMap": true, "incremental": true >, "include": ["src"], "exclude": ["node_modules"] > 

I’m experiencing the same issue. Looks like, for whatever reason, they took out children on the Component class

Читайте также:  Создать свою программу python

9 Answers 9

Although this answer is correct, I want to note that you absolutely don’t have to use this PropsWithChildren helper. (It is primarily useful for the codemod, not manual usage.)

Instead, I find it easier to define them manually.

Before

import * as React from 'react'; type Props = <>; const Component: React.FC = () =>

After

import * as React from 'react'; type Props = < children?: React.ReactNode >; const Component: React.FC = () =>

Or you can stop using React.FC altogether.

import * as React from 'react'; type Props = < children?: React.ReactNode >; function Component(: Props): React.ReactNode

In React, children is a regular prop and is not something special. So you need to define it just like you define all the other props. The previous typings that hid it were wrong.

I know this is a bit off-topic, but do you think ref will ever be a «regular prop» too? Typing ref , especially for components wrapping other components, is usually very difficult. React.ComponentProps would solve it if ref were a normal prop.

At some point, I think so — there are some changes we need to make before we can do that. I don’t see why typing it would be complicated though. That’s probably a separate discussion and you’re welcome to start it in the DefinitelyTyped repo.

That’s good to hear. The issue is related to typing forwardRef for a design system library. For const Box = styled(View) , I’m not sure how to make Box have the same ref type as View , whereas for the props this is easy with a generic. I’ll continue this on the DefinitelyTyped repo though, thanks for the prompt response!

Thanks for the help/answer. Where would one make this change in the code to work as it used to

What about VFC ? It all seemed pretty straightforward: FC added implicit children , VFC did not. Now in React 18 it seems both are the same because. It looks like I’ll be creating a new type CFC = FC> to restore that distinction.

How to resolve

No props

Before

import React from 'react'; const Component: React.FC = () =>

After

Create e.g. react.d.ts to define your helper type 1

import React from 'react'; export type ReactFCWithChildren = React.FC; 
import from './react'; const Component: ReactFCWithChildren = () =>
import React from 'react'; const Component: React.FC = () =>

With props

Before

import React from 'react'; interface Props < . >const Component: React.FC = () =>

After

import React from 'react'; interface Props < . >const Component: React.FC> = () =>
import React from 'react'; interface Props extends React.PropsWithChildren < . >const Component: React.FC = () =>

1 While defining children manually seems easy, it’s better to leverage types that are already prepared for you in @types package. When there are changes to the type in the future, it will automatically propagate from the lib everywhere in your code so you won’t have to touch it yourself.

Suppress warnings for some reason

You can override react types by creating react.d.ts file with following definition which would revert the type to @types/react v17

import * as React from '@types/react'; declare module 'react' < interface FunctionComponent> < (props: PropsWithChildren, context?: any): ReactElement | null; > > 

Why did FC signature change

children prop was removed from React.FunctionComponent ( React.FC ) so you have to declare it explicitly.

TS will tell you errors like

Type ‘< children: . ; >‘ has no properties in common with type ‘IntrinsicAttributes’.»

You can read why here. TLDR it prevents bugs like

const ComponentWithNoChildren: React.FC = () => <>Hello; . // passing children is wrong since component does not accept any  

The first option has ts error Generic type ‘PropsWithChildren’ requires 1 type argument(s). , but the second one is just perfect

This is great, what about third party libraries? I’ve been hacking it const ThirdPartyHack: any = ThirdParty

@MichaelJosephAubry unfortunately, you’ll have to wait for libraries to adapt this (my answer ideally :D)

This is breaking the React «promise» of backward compatibility from one version to the next, at least in regards with TypeScript typings. Indeed, one can’t update to the new typings if only one of their dependency is still on React 17. So if you have dependencies, you can only: — Update them all only when they are all using React 18 typings — Or not updating ANYTHING at all, because you can’t just update one dependency that’s on React 18 while the rest is on React 17 (again, only talking about typings).

Yes, the React.FC type has changed. But you can declare your own type with blackjack and react children.

My way is to create src/types/react.d.ts with content like this:

import React, < PropsWithChildren >from 'react'; export type ReactFCC = React.FC; 

Update #01

You can add default value for the T parameter:

import React, < PropsWithChildren >from 'react'; export type ReactFCC> = React.FC>; 
import React, < PropsWithChildren >from 'react'; export type ReactFCC = React.FC>; 

You can now choose not to specify a type in a ReactFCC generic without warnings.

export const Component: ReactFCC = props => < const < children >= props; /* . */ > 
export const Component: ReactFCC = props => < const < children >= props; /* . */ > 

Create your custom functional component type (a modification of FC ).

Lets name it FCC (Denoting:- Functional component with children 😉 )

// Put this in your global types.ts import < FC, PropsWithChildren >from "react"; // Custom Type for a React functional component with props AND CHILDREN export type FCC> = FC> 

Whenever you want children property in you Component’s props , use it like this:

// import FCC from types.ts const MyComponent: FCC = () => < return ( <> ) > 
interface MyCompoProps < prop1: string >const MyComponent: FCC = () => < return ( <> ) > 

PS This answer might look similar to @Garvae’s answer but his ReactFCC type should be written like ReactFCC> to prevent this following error:

Generic type ‘ReactFCC’ requires 1 type argument(s)

This error occurs when you are passing no props to the Component. children prop should be an optional prop. So giving those props a default <> value (i.e P = <> ) solves the issue.

As Dan points out in his answer, you may not need React.FC any more. Here’s an additional suggestion for if you choose to use a plain function .

Component without children

import * as React from 'react'; type Props = < >; export function Component(<>: Props) < . > // Valid test // Invalid 

Component with children required

import * as React from 'react'; type Props = < children: React.ReactNode >; export function Component(: Props) < . >test // Valid // Invalid 

Component with children optional

import * as React from 'react'; type Props = < children?: React.ReactNode >; export function Component(: Props) < . >test // Valid // Valid 

Using React.ReactNode as return type doesn’t seem like a good recommendation since rendering such a component from the top of another component results in: [tsserver 2786] [E] ‘Example’ cannot be used as a JSX component. Its return type ‘ReactNode’ is not a valid JSX element. Type ‘undefined’ is not assignable to type ‘Element | null’. Try this: function Example (): React.ReactNode < return >; function Other(): React.ReactNode

Omitting return type entirely might be better. tsserver will automatically warn about invalid return as long as there is code that tries to use the component. function Example () <>; You could put a usage in the same file like this, then tsserver will warn if invalid. You could even skip assigning to a variable.

Источник

React Children with TypeScript

The React children prop allows components to be composed together and is a key concept for building reusable components. Visually, we can think of it as a hole in the component where the consumer controls what is rendered. This post covers different approaches to strongly-typing this powerful and flexible prop with TypeScript.

React Children with TypeScript

Using the FC type

There is a standard React type, FC , that we can use on arrow function components. FC stands for Function Component, and it aliases a type called FunctionComponent .

type Props =  title: string, >; const Page: React.FCProps> = (< title, children, >) => ( div> h1>title>h1> children> div> );

FC is a generic type that takes in the specific type for all the component props. In the example above, we passed in a Props type alias containing a title prop. Alternatively, an interface could be used to define Props .

Notice that children isn’t defined in Props . Instead, it is already defined in the FC type. This is nice if you know this fact, but it might be a bit confusing if you don’t.

Explicitly defining the children prop type

If we explicitly define the children prop type, we have several different options for its type.

Let’s go through them one by one …

Using JSX.Element

Let’s try JSX.Element for starters:

type Props =  title: string, children: JSX.Element,>; const Page = ( title, children >: Props) => ( div> h1>title>h1> children> div> );

children is required at the moment. If we want to make this optional for the consumer of the component, we put a question mark( ? ) before the type annotation:

type Props =  title: string; children?: JSX.Element;>;

JSX.Element is good if the child is required to be a single React element. However, it doesn’t allow multiple children. So, we could make the following adjustment:

type Props =  title: string; children?: JSX.Element | JSX.Element[];>;

Using ReactChild

A downside of JSX.Element is that it doesn’t allow strings. So, we could add strings to the union type:

type Props =  title: string; children: | JSX.Element | JSX.Element[] | string | string[];>;

… this is getting out of hand!

Fortunately, there is a standard type called ReactChild that includes React elements, strings and numbers. So, we could widen the type for children as follows:

type Props =  title: string; children?: | React.ReactChild | React.ReactChild[];>;

Using ReactNode

React.ReactChild | React.ReactChild[] gives the breadth of values we need, but is a little verbose. ReactNode is a more terse option:

type Props =  title: string; children?: React.ReactNode;>;

ReactNode allows multiple elements, strings, numbers, fragments, portals, …

The FC generic type uses ReactNode under the hood as well.

Class components

What if we are using a class component? Let’s explore this:

type Props =  title: string, >; export class Page extends React.ComponentProps>  render()  return ( div> h1>this.props.title>h1> this.props.children> div> ); > >

Like FC , the Component type automatically includes the children prop.

If we hover over the children prop, we discover the type it has been given:

Class children

So, the type of the children prop in a class component is ReactNode as well.

Wrap up

If we are using function components with the FC type, the children prop is already typed. If we explicitly type the children prop, the ReactNode is generally the best choice.

Did you find this post useful?

If you to learn more about using TypeScript with React, you may find my course useful:

Источник

Оцените статью