skip to Main Content

We are trying to migrate to react 18.

The children prop was removed from React.FunctionComponent (React.FC) so you have to declare it explicitly in your component properties.

But we want to keep the old React.FunctionComponent with children behavior. So, we are tryibg to override react types by creating a custom type definition file index.d.ts. This way we wont have to change 100s of components manually.

import * as React from '@types/react';

declare module 'react' {
  //type PropsWithChildren<P> = P & { children?: ReactNode | undefined };
  interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
  }
}

We have components like below.


interface IProps {
  age: number
}

const Demo: React.FunctionComponent<IProps> = ({
  children,
  age
}) => (
  <>
    {children}
    {age}
  </>
);

<Demo age={10}>Hi</Demo>

After overriding react types we are now getting following error in above Demo component.

Property 'children' does not exist on type 'IProps | PropsWithChildren<IProps>'

PropsWithChildren is defined in React 18 as below.

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

Below is the codesandbox link

https://codesandbox.io/s/quizzical-goodall-9d9s3j?file=/src/Demo.tsx

Ref :- React 18 TypeScript children FC

Any help?

2

Answers


  1. General answer – you need to follow new rules of React 18, and you should change 100s files 🤷

    The normal solution, for this case, will be to manually update component props interfaces to add children only where’s needed. But if it’s overkill for you, then add it to every component:

    1. Use your IDE global "find & replace" tool
      find&replace
    2. Replace all
      React.FunctionComponent<
      with
      React.FunctionComponent<{children?: ReactNode | undefined } &

    It’ll convert all your functional components to be like:

    interface IProps {
      age: number
    }
    
    const Demo: React.FunctionComponent<{children?: ReactNode | undefined } & IProps> = ({
      children,
      age
    }) => (
      <>
        {children}
        {age}
      </>
    );
    
    <Demo age={10}>Hi</Demo>
    

    @alissa: it’s nicer to do React.FunctionComponent<PropsWithChildren<IProps>> then is something changes in the children type your code will be in sync

    Replace all
    React.FunctionComponent<(w+)>
    with
    React.FunctionComponent<PropsWithChildren<$1>>

    Login or Signup to reply.
  2. It seems that it’s impossible to override React.FunctionComponent in a mannar that will replace the props parameter type.

    The merging mechanism of a function interface, if you declare the same interface multiple times, is to add overloads of the function – create a more inclusive type. see docs

    for example, if you declare interface A twice like

    interface A {
      (a: string): void
    }
    
    interface A {
      (a: number): void
    }
    

    and then use interface A

    const foo: A = (a) => {}

    the type of a will be number | string. and not just number.

    Which makes sense, because someone might have already written some code assuming that A receives a parameter of type string.

    Though I did manage to create a new type with the correct props

    interface FunctionComponentWithChildren<P> extends Omit<React.FunctionComponent<P>, "parameters"> {
      (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
    }
    

    I think I would add a FunctionComponentWithChildren type and change my code to use it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search