Skip to content

Commit 1a6f39f

Browse files
committed
Updated with explicit children
1 parent adbefd5 commit 1a6f39f

8 files changed

+18
-92
lines changed

docs/advanced/patterns_by_usecase.md

+5-77
Original file line numberDiff line numberDiff line change
@@ -346,79 +346,6 @@ class List<T> extends React.PureComponent<Props<T>, State<T>> {
346346
}
347347
```
348348

349-
### Generic components with children
350-
351-
`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:
352-
353-
```tsx
354-
interface WrapperProps<T> {
355-
item: T;
356-
renderItem: (item: T) => React.ReactNode;
357-
}
358-
359-
/* Property 'children' does not exist on type 'WrapperProps<T>'. */
360-
const Wrapper = <T extends {}>(props: WrapperProps<T>) => {
361-
return (
362-
<div>
363-
{props.renderItem(props.item)}
364-
{props.children}
365-
</div>
366-
);
367-
};
368-
369-
/*
370-
Type '{ children: string; item: string; renderItem: (item: string) => string; }' is not assignable to type 'IntrinsicAttributes & WrapperProps<string>'.
371-
Property 'children' does not exist on type 'IntrinsicAttributes & WrapperProps<string>'.
372-
*/
373-
374-
const wrapper = (
375-
<Wrapper item="test" renderItem={(item) => item}>
376-
{test}
377-
</Wrapper>
378-
);
379-
```
380-
381-
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615GWgAWwADZamkrOjqFuHhQAvhQUAPQAVHC8EFywAJ4EvgFBSNT4cFoQSPxw1BDwSAAewPzwENRwMOlcBGwcaSkCIqL4DnAJcRRoDXWs7Jz01nAicNV02qUSUaKGYHz8Su2TUF1CYpY2kupEMACuUI2G6jKCWsAAbqI3MpLrqfwOmjpQ+qZrGwcJhA5hiXleMgk7wEDmygU0YIhgji9ye6nMniinniCQowhazHwEjgcNy1CUdSgNAA5ipZAY4JSaXTvnoGcYGUzqNTDuIubS4FECrUyhU4Ch+PxgNTqCgAEb+ZgwCBNAkEXS0KnUKVoACCMBgVLlZzopQAZOMOjwNoJ+b0HOouvRmlk-PC8gUiiVRZUamMGqrWvgNYaaDr9aHjaa4Bbtp0bXa+hRBrFyCNtfBTfArHBDLyZqjRAAJJD+fwqrPIwvDUbwADuEzS02u4MEcamwKsACIs12NHkfn8QFYJMDrOJgSsXhIs4iZnF21BnuQMUA)
382-
383-
To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed):
384-
385-
```tsx
386-
interface WrapperProps<T> {
387-
item: T;
388-
renderItem: (item: T) => React.ReactNode;
389-
children: string; // The component will only accept a single string child
390-
}
391-
392-
const Wrapper = <T extends {}>(props: WrapperProps<T>) => {
393-
return (
394-
<div>
395-
{props.renderItem(props.item)}
396-
{props.children}
397-
</div>
398-
);
399-
};
400-
```
401-
402-
or wrap the type of the props in `React.PropsWithChildren` (this is what `React.FC<>` does):
403-
404-
```tsx
405-
interface WrapperProps<T> {
406-
item: T;
407-
renderItem: (item: T) => React.ReactNode;
408-
}
409-
410-
const Wrapper = <T extends {}>(
411-
props: React.PropsWithChildren<WrapperProps<T>>
412-
) => {
413-
return (
414-
<div>
415-
{props.renderItem(props.item)}
416-
{props.children}
417-
</div>
418-
);
419-
};
420-
```
421-
422349
## Typing Children
423350

424351
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
593520
type AnchorProps = React.AnchorHTMLAttributes<HTMLAnchorElement>;
594521
type RouterLinkProps = Omit<AnchorProps, "href">;
595522

596-
interface Button {
523+
interface ButtonProps {
597524
as: React.ComponentClass | "a";
525+
children?: React.ReactNode;
598526
}
599527

600-
const Button: React.FunctionComponent<Button> = (props) => {
528+
const Button: React.FunctionComponent<ButtonProps> = (props) => {
601529
const { as: Component, children, ...rest } = props;
602530
return (
603531
<Component className="button" {...rest}>
@@ -806,7 +734,7 @@ You can implement this by function overloads:
806734

807735
```tsx
808736
type CommonProps = {
809-
children: React.ReactNode;
737+
children?: React.ReactNode;
810738
miscProps?: any;
811739
};
812740

@@ -901,7 +829,7 @@ Sometimes you will want to write a function that can take a React element or a s
901829
```tsx
902830
export interface Props {
903831
label?: React.ReactNode;
904-
children: React.ReactNode;
832+
children?: React.ReactNode;
905833
}
906834
export const Card = (props: Props) => {
907835
return (

docs/basic/getting-started/basic-type-examples.md

+3-7
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,8 @@ Relevant for components that accept other React components as props.
5959

6060
```tsx
6161
export declare interface AppProps {
62-
children1: JSX.Element; // bad, doesnt account for arrays
63-
children2: JSX.Element | JSX.Element[]; // meh, doesn't accept strings
64-
children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility
65-
children4: React.ReactChild[]; // better, accepts array children
66-
children: React.ReactNode; // best, accepts everything (see edge case below)
67-
functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop type
62+
children?: React.ReactNode; // best, accepts everything React can render
63+
childrenElement: JSX.Element; // A single React element
6864
style?: React.CSSProperties; // to pass through style props
6965
onChange?: React.FormEventHandler<HTMLInputElement>; // form events! the generic parameter is the type of event.target
7066
// more info: https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#wrappingmirroring
@@ -80,7 +76,7 @@ Before the [React 18 type updates](https://github.com/DefinitelyTyped/Definitely
8076

8177
```tsx
8278
type Props = {
83-
children: React.ReactNode;
79+
children?: React.ReactNode;
8480
};
8581

8682
function Comp({ children }: Props) {

docs/basic/getting-started/context.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,10 @@ interface ProviderStore {
252252

253253
const Context = React.createContext({} as ProviderStore); // type assertion on empty object
254254

255-
class Provider extends React.Component<{}, ProviderState> {
255+
class Provider extends React.Component<
256+
{ children?: React.ReactNode },
257+
ProviderState
258+
> {
256259
public readonly state = {
257260
themeColor: "red",
258261
};

docs/basic/getting-started/error-boundaries.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ If you don't want to add a new npm package for this, you can also write your own
1616
import React, { Component, ErrorInfo, ReactNode } from "react";
1717

1818
interface Props {
19-
children: ReactNode;
19+
children?: ReactNode;
2020
}
2121

2222
interface State {

docs/basic/getting-started/forward-create-ref.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class CssThemeProvider extends React.PureComponent<Props> {
1919
`forwardRef`:
2020

2121
```tsx
22-
type Props = { children: React.ReactNode; type: "submit" | "button" };
22+
type Props = { children?: React.ReactNode; type: "submit" | "button" };
2323
export type Ref = HTMLButtonElement;
2424
export const FancyButton = React.forwardRef<Ref, Props>((props, ref) => (
2525
<button ref={ref} className="MyClassName" type={props.type}>
@@ -34,7 +34,7 @@ export const FancyButton = React.forwardRef<Ref, Props>((props, ref) => (
3434
This was done [on purpose](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/43265/). You can make it immutable if you have to - assign `React.Ref` if you want to ensure nobody reassigns it:
3535

3636
```tsx
37-
type Props = { children: React.ReactNode; type: "submit" | "button" };
37+
type Props = { children?: React.ReactNode; type: "submit" | "button" };
3838
export type Ref = HTMLButtonElement;
3939
export const FancyButton = React.forwardRef(
4040
(

docs/basic/getting-started/portals.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Using `ReactDOM.createPortal`:
99
const modalRoot = document.getElementById("modal-root") as HTMLElement;
1010
// assuming in your html file has a div with id 'modal-root';
1111

12-
export class Modal extends React.Component {
12+
export class Modal extends React.Component<{ children?: React.ReactNode }> {
1313
el: HTMLElement = document.createElement("div");
1414

1515
componentDidMount() {
@@ -39,7 +39,7 @@ import { createPortal } from "react-dom";
3939

4040
const modalRoot = document.querySelector("#modal-root") as HTMLElement;
4141

42-
const Modal: React.FC<{}> = ({ children }) => {
42+
const Modal: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
4343
const el = useRef(document.createElement("div"));
4444

4545
useEffect(() => {

docs/hoc/full-example.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ The goal is to have the props available on the interface for the component, but
2222

2323
```ts
2424
interface Props extends WithThemeProps {
25-
children: React.ReactNode;
25+
children?: React.ReactNode;
2626
}
2727

2828
class MyButton extends React.Component<Props> {

docs/hoc/react-hoc-docs.md

-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ function CommentList({ data }: WithDataProps<typeof comments>) {
6868
}
6969
interface BlogPostProps extends WithDataProps<string> {
7070
id: number;
71-
// children: ReactNode;
7271
}
7372
function BlogPost({ data, id }: BlogPostProps) {
7473
return (

0 commit comments

Comments
 (0)