DEV Community

Michael Grechka
Michael Grechka

Posted on • Updated on

React types you didn't know

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:

function SomeComponent(props: SomeComponentProps): JSX.Element {
   ...
}
Enter fullscreen mode Exit fullscreen mode

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:

type FC<P = {}> = FunctionComponent<P>;
Enter fullscreen mode Exit fullscreen mode

Let's see what this FunctionComponent is:

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;
}
Enter fullscreen mode Exit fullscreen mode

Here we also need to check the PropsWithChildren type to make sure what we will pass to the component's props:

type PropsWithChildren<P> = P & { children?: ReactNode };
Enter fullscreen mode Exit fullscreen mode

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

const SomeComponent: FC<SomeComponentProps> = ({ message }) => (
  <div>{message}</div>
);
Enter fullscreen mode Exit fullscreen mode

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:

SomeComponent.displayName = 'SomeComponent';
Enter fullscreen mode Exit fullscreen mode

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:

type SomeType = HTMLAttributes<HTMLInputElement>;
Enter fullscreen mode Exit fullscreen mode

This type is not much reusable because we cannot get props of the custom component and this is where ComponentProps comes:

 // regular HTML input element
type InputElementProps = ComponentProps<'input'>;

// ComponentProps works for both regular elements and the custom ones
type CustomInputProps = ComponentProps<typeof CustomInput>;
Enter fullscreen mode Exit fullscreen mode

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:

interface SomeComponentProps {
  display: CSSProperties['display']
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)