7 TypeScript Utility Types For React Developers

June 06, 2022 - 6 minutes

It’s almost unthinkable to do React development without TypeScript. The two technologies have been established to be an extremely powerful duo, enabling developers to create high-quality and maintainable React applications at scale. TypeScript plays an integral part in this process.

However, being a good React developer doesn’t automatically translate into being a good TypeScript developer. Writing quality TypeScript code is a separate skill, just like writing quality React code. This is even more prominent when combining the two skills together.

To help with this, this article goes over 7 different TypeScript utility types. Based on personal experience, these utility types are extremely helpful to React developers on a daily basis. They’re applicable in common React development practises or geared towards React development. Mastering them will significantly improve your qualities as a TypeScript React developer.

Pick

The Pick utility type allows you to construct a new object type based on an existing object type or interface. It accepts two arguments, the existing object type or interface and a string literal or union of string literals of keys that should be included in the resulting object type.

For React developers, this is especially useful when a component’s props share typings with another component or data type. Instead of specifying all the types manually in the new type, you can use the Pick utility. This also creates an explicit connection between the types and automatically aligns the types across all the related typings.

import { PropsWithChildren } from "react";
import { SomeOtherComponent, SomeOtherComponentProps } from "./someOtherComponent";

type ComponentProps = Pick<Something, "propA" | "propB" | "children"> & {
	wrapperClassName?: string;
}

export const Component = (props: ComponentProps) => (
	<div className={props.wrapperClassName}>
		<SomeOtherComponent propA={props.propA} propB={props.propB}>
			{props.children}
		</SomeOtherComponent>
	</div>
); 

Omit

The Omit utility type is very similar to the previously discussed Pick utility type but basically does the inverse. Instead of specifying with keys to include in the constructed object type or interface, you specify the ones that should be excluded. This becomes useful when you want to reuse a large part of an existing object type or only want to exclude a small number of them.

In terms of use cases in React development, they’re similar to the use cases for Pick. Think of taking over types from another component’s props or data type. However, the minor difference is that Omit is more explicit about the entries that should be excluded rather than included.

import { PropsWithChildren } from "react";
import { SomeOtherComponent, SomeOtherComponentProps } from "./someOtherComponent";

type ComponentProps = Omit<Something, "propC" | "propD"> & {
	wrapperClassName?: string;
}

export const Component = (props: ComponentProps) => (
	<div className={props.wrapperClassName}>
		<SomeOtherComponent propA={props.propA} propB={props.propB}>
			{props.children}
		</SomeOtherComponent>
	</div>
); 

Partial

The Partial utility type allows you to construct a new object type where all the properties in the original object type or interface are set to optional. Basically, it creates a subset of typings compared to the original one.

For React development, this can be especially useful inside of tests or utility functions that provide props for components. In those scenarios, it’s often unwanted to provide all the props at once. Different parts of the props might come from different sources or functions, while all of them together complete the set of props.

import { mount } from "enzyme";
import { SomeComponent, SomeComponentProps } from "./someComponent";

function mountComponent(props: Partial<SomeComponentProps>): ReactWrapper {
  return mount(
    <SomeComponent variant="normal" title="Test Title" {...props} />
  );
}

describe("SomeComponent", () => {
  test("works like it should", () => {
    const onChangeMock = jest.fn();
    const component = mountComponent({ onChange: onChangeMock });
  });
});

NonNullable

Oftentimes in React, you’ll be dealing with values that can be optional or nullable. Think of optional props, potentially missing data, or filtering for missing values. In all of these cases, TypeScript will let you know that these variables can also be undefined or null.

But in certain scenarios, you want to specify towards TypeScript that it isn’t possible or that they can’t be undefined or null. Think of initialising a component’s prop that is marked as optional, but is definitely set in the component that uses it. In those cases, the NonNullable utility type can be used to construct a new type without null and undefined.

type OtherComponentProps = {
  // ...
  onChange?: (value: number) => void;
};

// -----

const Component = () => {
	// We know for sure it's not nullable as we're initialising it, but TypeScript doesn't so we have to help it.
  const changeValue = useCallback<NonNullable<OtherComponentProps["onChange"]>>(
    (value) => {
      if (value > 0) {
        doSomethingWithTheValue(value);
      }
    },
    []
  );

  return <SomeOtherComponent onChange={changeValue} />;
};

React.ComponentProps

Sometimes you don’t have access to the TypeScript typings of a component’s prop, but only the component itself. Think of a component from an external library or when it’s not desired to expose the component’s props typing to the outside world. While it’s understandable, it makes it difficult to reuse or extend upon existing typings. Implementing them manually would introduce redundancy and increase the maintenance burden.

Instead, you can use React’s ComponentProps utility type to get access to the props’ typing of a component in those scenarios. As the name indicates, it accepts the type of a component as its arguments and will construct a new type with the types of the component’s props.

import { ComponentProps } from "react";
import { ExternalComponent } from "external-lib";

type InternalComponentProps = ComponentProps<typeof ExternalComponent> & {
  outerClassName: string;
};

const InternalComponent = ({
  outerClassName,
  ...restProps
}: InternalComponentProps) => (
  <div className={outerClassName}>
    <ExternalComponent {...restProps} />
  </div>
);

React.MouseEventHandler

An integral part of React applications is interactivity for users. A common way of implementing this is done through event handlers, most prominently mouse events. The difficult part of typing these event handlers is the fact that there are multiple parts to it. There is the entire event handler itself, the event that is provided to the handler, and a potential return value.

When starting out with TypeScript in React, it’s tempting to type each of those parts individually. While that works as a short term solution, it’s not a solution that scales well into the future. Instead, you can make use of React’s utility type MouseEventHandler. It’s made specifically for these use cases and accepts an optional argument for the targetted HTML element.

import { MouseEventHandler, PropsWithChildren } from "react";

type ComponentProps = {
  title: string;
	caption: string;
  onClick: MouseEventHandler<HTMLButtonElement>;
};

const Component = (props: ComponentProps) => (
  <div>
    <h2>{props.title}</h2>
    <button onClick={props.onClick}>{props.caption}</button>
  </div>
);

React.PropsWithChildren

One of the greatest aspects to React is its composable nature which allows components to be nested inside of other components from the outside. Most of the time, this is done through the natural children prop from React.

One of the ways to tell TypeScript that a component accepts the children prop is by explicitly stating it in the props’ typing. Something along the lines of children: ReactNode will look familiar to many React developers that have adopted TypeScript.

However, another way to implement this is through React’s utility type PropsWithChildren. It automatically constructs a new type with the respective typing, with the addition of marking it as optional and nullable.

import { MouseEventHandler, PropsWithChildren } from "react";

type ComponentProps = {
  title: string;
  onClick: MouseEventHandler<HTMLButtonElement>;
};

// `props.children` exists because of `PropsWithChildren`.
const Component = (props: PropsWithChildren<ComponentProps>) => (
  <div>
    <h2>{props.title}</h2>
    <button onClick={props.onClick}>{props.children}</button>
  </div>
);

Final Thoughts

In modern React development, it’s almost unthinkable to not have TypeScript involved. It improves the quality and maintainability of the code tremendously. However, being able to use TypeScript properly is an entirely separate skill on its own. This especially applies to the combination of TypeScript and React.

To help with this, this article went over 7 different TypeScript utility types that are useful to React developers. These utilities are either useful in common React development practises or geared specifically towards React development. Understanding and mastering these types will significantly improve the quality of your typed React code.


After graduation, my career is entirely centered around learning and improving as a developer. I’ve began working full time as a React developer and I’ll be blogging about everything that I encounter and learn during this journey. This will range from improving communicational skills in a technical environment, becoming a better developer, improving technical skills in React and JavaScript, and discussing career related topics. In all of my posts, the focus will be on my personal experiences, learnings, difficulties, solutions (if present), and also flaws.

If you’re either interested in these topics, more personalised technical stories, or the perspective of a learning developer, you can follow me either on Twitter or at Dev to stay up to date with my blogposts. I’m always learning, so stay tuned for more stories! 🎉