Unsafe 'PropsWithChildren' Utility type in React TypeScript App
Tags: typescript, frontend, react, node
Published: 2023-07-15 08:15:09

As React developer, we are all familiar with creating components, whether as classes or functions, to add features to web pages. 

One of the common cases in React is a component that has **JSX Children**. When you want to nest content inside a JSX tag, the parent component will receive that content in a prop called `children`. 

For instance, the Modal component below will receive `children` prop set to <Image /> and render it in a wrapper div.

```react
// React JavaScript

function Modal(props) {
  const { children } = props;

  return <div className="modal">{children}</div>;
}

function Page() {
  return (
    <Modal>
      <title>Submit Form</title>
      <form onSubmit={handleSubmit}>
        <input type="text" />
        <input type="submit" />
      </form>
    </Modal>
  );
}
```

If it wants to be converted to TypeScript, most of developers use `PropsWithChildren` utility type from `@types/react` package. 

```react
// React TypeScript

import type { PropsWithChildren } from "react";

// props: {
//    children?: React.ReactNode;
// }
function Modal(props: PropsWithChildren<{}>) {
  const { children } = props;

  return <div className="modal">{children}</div>;
}

function Page() {
  return (
    <Modal>
      <title>Submit Form</title>
      <form onSubmit={handleSubmit}>
        <input type="text" />
        <input type="submit" />
      </form>
    </Modal>
  );
}
```
It helps TypeScript understand that the component should have the `children` prop.
<br>
## Problems

Sounds good so far, right? But here's where things get weird. 

Let me highlight the code.

```
// props: {
//    children?: React.ReactNode; ❌
// }
```

```react
// Unexpected ❌

import type { PropsWithChildren } from "react";

// props: {
//    children?: React.ReactNode;
// }
function Modal(props: PropsWithChildren<{}>) {
  const { children } = props;

  return <div className="card">{children}</div>;
}

function Page() {
  return <Modal />;
}
```

The `children` is supposed to be **required**. That means we must provide `children` when using `Modal` component. But, guess what? It becomes **optional**! Rendering `Modal` component without `children` is now even possible 😨.

Let's check what's inside `PropsWithChildren`. Here it is referring to [@types/react Github](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L835).

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

Turns out the culprit is `@types/react` itself 😵.

This little bug might not seem like a big deal at first, but trust me, it can cause some serious problems and mess up with your business logic. And the worst part? Neither React nor TypeScript will show any errors, leaving you scratching your head when things go wrong 😵‍💫.
<br>
## Solutions

So, what can we do about this? Let's explore some solutions!
<br>
### 1. Never Trust and Always Check "third-party" code

As you know earlier, the culprit is `@types/react` itself. This is not the first time. Previously, developers had a problem with `React.FC`. Some of them even posted about it. 

[TypeScript + React: Why I don't use React.FC](https://fettblog.eu/typescript-react-why-i-dont-use-react-fc/)
[Why you probably shouldn’t use React.FC to type your React components](https://blog.raccoons.be/react-fc-opinion)
[Why You Should Probably Think Twice About Using React.FC](https://spin.atomicobject.com/2022/01/04/think-twice-react-fc/)

All those fancy "magic" codes will eventually lose their "magic" over time. So, you should always check what's behind the code.
<br>
### 2. Create your own Utility type

Since the `PropsWithChildren` from `@types/react` is not that hard, why don't you create it yourself?

```typescript
type PropsWithChildren<P = unknown> = P & { children: ReactNode };
```

`PropsWithChildren` utility type is for components that have `children` prop and it's required.

```typescript
type PropsWithOptionalChildren<P = unknown> = P & { children?: ReactNode };
```

`PropsWithOptionalChildren` utility type is for components that have `children` prop, but it's optional.

It looks reliable and unambiguous. ✅

## Conclusion
Remember these tips and you'll save yourself from headaches. I hope these tips come in handy for your projects! ✨