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",
tsconfig.json
{
"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"]
}
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.
import * as React from 'react';
type Props = {};
const Component: React.FC<Props> = ({children}) => {...}
import * as React from 'react';
type Props = {
children?: React.ReactNode
};
const Component: React.FC<Props> = ({children}) => {...}
That is all that's needed.
Or you can stop using React.FC altogether.
import * as React from 'react';
type Props = {
children?: React.ReactNode
};
function Component({children}: 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.
import React from 'react';
const Component: React.FC = ({children}) => {...}
Create e.g. react.d.ts to define your helper type 1
export type PropsWithChildrenOnly = PropsWithChildren<unknown>;
export type ReactFCWithChildren = React.FC<PropsWithChildrenOnly>;
import React from 'react';
import { PropsWithChildrenOnly, ReactFCWithChildren } from './react';
const Component: React.FC<PropsWithChildrenOnly> = ({children}) => {...}
or
const Component: ReactFCWithChildren = ({children}) => {...}
import React from 'react';
interface Props {
...
}
const Component: React.FC<Props> = ({children}) => {...}
import React, { PropsWithChildren } from 'react';
interface Props {
...
}
const Component: React.FC<PropsWithChildren<Props>> = ({children}) => {...}
or
interface Props extends PropsWithChildren<unknown> {
...
}
const Component: React.FC<Props> = ({children}) => {...}
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.
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<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
}
}
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</>;
...
<ComponentWithNoChildren>
// passing children is wrong since component does not accept any
<UnusedChildrenSinceComponentHasNoChildren />
</ComponentWithNoChildren>
It looks like the children attribute on the typescript typings were removed.
I had to manually add children to my props; There is probably a better solution to fix this, but in the interim, this works.