diff --git a/README.md b/README.md index f345d25d..bb6f7634 100644 --- a/README.md +++ b/README.md @@ -339,7 +339,7 @@ const Title: React.FunctionComponent<{ title: string }> = ({ In [@types/react 16.9.48](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46643), the `React.VoidFunctionComponent` or `React.VFC` type was added for typing `children` explicitly. However, please be aware that `React.VFC` and `React.VoidFunctionComponent` were deprecated in React 18 (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59882), so this interim solution is no longer necessary or recommended in React 18+. -Please use regular function components or `React.VFC` instead. +Please use regular function components or `React.FC` instead. ```ts type Props = { foo: string }; @@ -1178,9 +1178,9 @@ export declare interface AppProps { ```
-Small React.ReactNode edge case +Small React.ReactNode edge case before React 18 -This code typechecks but has a runtime error: +Before the [React 18 type updates](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210), this code typechecked but had a runtime error: ```tsx type Props = { @@ -1191,16 +1191,16 @@ function Comp({ children }: Props) { return
{children}
; } function App() { - return {{}}; // Runtime Error: Objects not valid as React Child! + // Before React 18: Runtime error "Objects are not valid as a React child" + // After React 18: Typecheck error "Type '{}' is not assignable to type 'ReactNode'" + return {{}}; } ``` -This is because `ReactNode` includes `ReactFragment` which allows a `{}` type, which is [too wide](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/37596#issue-480260937). Fixing this would break a lot of libraries, so for now you just have to be mindful that `ReactNode` is not absolutely bulletproof. +This is because `ReactNode` includes `ReactFragment` which allowed type `{}` before React 18. [Thanks @pomle for raising this.](https://github.com/typescript-cheatsheets/react/issues/357) -With the [React 18 type updates](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210), `{}` is no longer allowed in `ReactFragment`. -
diff --git a/docs/advanced/patterns_by_usecase.md b/docs/advanced/patterns_by_usecase.md index b18964ca..5380f3e8 100644 --- a/docs/advanced/patterns_by_usecase.md +++ b/docs/advanced/patterns_by_usecase.md @@ -346,79 +346,6 @@ class List extends React.PureComponent, State> { } ``` -### Generic components with children - -`children` is usually not defined as a part of the props type. Unless `children` are explicitly defined as a part of the `props` type, an attempt to use `props.children` in JSX or in the function body will fail: - -```tsx -interface WrapperProps { - item: T; - renderItem: (item: T) => React.ReactNode; -} - -/* Property 'children' does not exist on type 'WrapperProps'. */ -const Wrapper = (props: WrapperProps) => { - return ( -
- {props.renderItem(props.item)} - {props.children} -
- ); -}; - -/* -Type '{ children: string; item: string; renderItem: (item: string) => string; }' is not assignable to type 'IntrinsicAttributes & WrapperProps'. - Property 'children' does not exist on type 'IntrinsicAttributes & WrapperProps'. -*/ - -const wrapper = ( - item}> - {test} - -); -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615GWgAWwADZamkrOjqFuHhQAvhQUAPQAVHC8EFywAJ4EvgFBSNT4cFoQSPxw1BDwSAAewPzwENRwMOlcBGwcaSkCIqL4DnAJcRRoDXWs7Jz01nAicNV02qUSUaKGYHz8Su2TUF1CYpY2kupEMACuUI2G6jKCWsAAbqI3MpLrqfwOmjpQ+qZrGwcJhA5hiXleMgk7wEDmygU0YIhgji9ye6nMniinniCQowhazHwEjgcNy1CUdSgNAA5ipZAY4JSaXTvnoGcYGUzqNTDuIubS4FECrUyhU4Ch+PxgNTqCgAEb+ZgwCBNAkEXS0KnUKVoACCMBgVLlZzopQAZOMOjwNoJ+b0HOouvRmlk-PC8gUiiVRZUamMGqrWvgNYaaDr9aHjaa4Bbtp0bXa+hRBrFyCNtfBTfArHBDLyZqjRAAJJD+fwqrPIwvDUbwADuEzS02u4MEcamwKsACIs12NHkfn8QFYJMDrOJgSsXhIs4iZnF21BnuQMUA) - -To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed): - -```tsx -interface WrapperProps { - item: T; - renderItem: (item: T) => React.ReactNode; - children: string; // The component will only accept a single string child -} - -const Wrapper = (props: WrapperProps) => { - return ( -
- {props.renderItem(props.item)} - {props.children} -
- ); -}; -``` - -or wrap the type of the props in `React.PropsWithChildren` (this is what `React.FC<>` does): - -```tsx -interface WrapperProps { - item: T; - renderItem: (item: T) => React.ReactNode; -} - -const Wrapper = ( - props: React.PropsWithChildren> -) => { - return ( -
- {props.renderItem(props.item)} - {props.children} -
- ); -}; -``` - ## Typing Children Some API designs require some restriction on `children` passed to a parent component. It is common to want to enforce these in types, but you should be aware of limitations to this ability. @@ -593,11 +520,12 @@ If you want to conditionally render a component, sometimes is better to use [Rea type AnchorProps = React.AnchorHTMLAttributes; type RouterLinkProps = Omit; -interface Button { +interface ButtonProps { as: React.ComponentClass | "a"; + children?: React.ReactNode; } -const Button: React.FunctionComponent