React types you didn't know
Tags: typescript, react, programming
Published: 2021-09-08 20:55:38

After working in several companies, I've noticed that the code is always typed differently and this can lead to some issues with typings in the future.

I want to show you how to make code better typed the way you probably didn't know.

## Function Component type

First of all, we have to figure out what is the best way to define a function component.

Usually developers do initialise components using default functions so the code would look like this:

```tsx
function SomeComponent(props: SomeComponentProps): JSX.Element {
   ...
}
```

Such approach has several disadvantages:
- You always need to write the return type
- If your component accepts any children you need to define the `children` property in your Props type and set the type union type like `null | ReactNode | ReactNode[]` or `null | ReactElement`
– Since you need to use one of types above for your children, you have to import them too

All of these problems make your code more "boilerplaty" and make your code less readable spending more time than you can.

The best solution for this is `FC` or `FunctionComponent` types. `FC` is just a shorthand for `FunctionComponent` – You can check this [here](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d195fb003a300937e6da60bc3d7e7bff548db519/types/babel-plugin-react-html-attrs/index.d.ts#L554):
```tsx
type FC<P = {}> = FunctionComponent<P>;
```

Let's see what this `FunctionComponent` is:
```tsx
interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
    propTypes?: WeakValidationMap<P> | undefined;
    contextTypes?: ValidationMap<any> | undefined;
    defaultProps?: Partial<P> | undefined;
    displayName?: string | undefined;
}
```
Here we also need to check the `PropsWithChildren` type to make sure what we will pass to the component's props:

```tsx
type PropsWithChildren<P> = P & { children?: ReactNode };
```

Now let's see how FC-typed component looks like:

```tsx
const SomeComponent: FC<SomeComponentProps> = ({ message }) => (
  <div>{message}</div>
);
```
Current solution solves such problems:
- We don't need to define children¹ type anymore
- We separate values from types moving generic type to the beginning making the code more readable
- We don't need to write a function return type
- Since we are using arrow function², we can omit curly braces in some cases and return jsx directly
- Using the same component type everywhere we prevent inconsistency of types that can lead to waste of time because of type definition instead of spending it to solve actual problems

1 - Keep in mind that it is planned to remove `children` as default property from `FunctionComponent` in `@types/react@^18.0.0`. In the React 18 you should define children property manually and set `ReactNode` type to it.
2 – If you are using React Developer Tools you should notice that arrow functions don't have `displayName` by default so you should define it manually:
```tsx
SomeComponent.displayName = 'SomeComponent';
```

## Type of regular HTML props / attributes

You've probably had problems with props definition for components that pass most of their props further to the regular html element.

Previously I've seen solutions like importing `HTMLAttributes` from `react` module and then passing `HTML...Element` type to it as a generic argument:
```tsx
type SomeType = HTMLAttributes<HTMLInputElement>;
```

This type is not much reusable because we cannot get props of the custom component and this is where `ComponentProps` comes:
```tsx
 // regular HTML input element
type InputElementProps = ComponentProps<'input'>;

// ComponentProps works for both regular elements and the custom ones
type CustomInputProps = ComponentProps<typeof CustomInput>;
```

## CSS property type

If you use css-in-js solutions, sometimes you want your component to accept certain CSS-properties passed deeper to the component's styles.

You can just set the property type as a type union: `number | string` but it is unspecific and can lead to unpredicted behaviour because of typos when using such properties as `position`, `flex-direction`, etc.

The better solution is to use `CSSProperties` type exported from `react` module:
```tsx
interface SomeComponentProps {
  display: CSSProperties['display']
}
```

Such little things may improve the quality of your code and prevent you from having pain with Typescript.