Using utility types for transforming TypeScript types

TypeScript provides some built-in utility types that help facilitate transformations of types from one form to another.

These utilities are available globally. They can be quite handy in various situations.

TypeScript generics

- Advertisement -

Before understanding the TypeScript utility types, it is important to understand type aliases and generics. You can create type aliases for any existing type in TypeScript.

type MyString = string;

let helloWorldMessage: MyString = 'Hello Wisdom Geek';

Type generics are used to create reusable type aliases. Let us say we had an identity function which returns whatever value is passed back in:

Type generics are used to create reusable type aliases. Let us say we had an identity function which returns whatever value is passed back in:

const identity = (arg: string): string => arg;

What if we wanted to use the function for a number? We could replace the specific type by any.

const identity = (arg: any): any => arg;

But that reduces the type information of the argument and thus, the whole benefit of using TypeScript gets lost. We want to capture the type of the argument in a way that we can use it to denote the return type. This is where generics come into the picture. We will use a type variable that works on types instead of values.

const identity = (arg: Type): Type => arg;

We specify the type of the function when we are invoking it:

const output = identity<string>("Hello Wisdom Geek");

Now that we know these basics, let us dive into the built-in utility type functions in TypeScript.

Built-in utility types in TypeScript

Before we jump into the utility types, it is important to note that these are available 4.0 onwards without the need of any additional packages.

Partial

Partial constructs a type with all properties of the input type as optional. For example:

type BlogPost = {
  title: string;
  author: string;
}

type PartialBlogPost = Partial<BlogPost>;
/* same as {
  title?: string;
  author?: string;
} */

A common use case for this would be while updating an item, you might want to provide a subset of the properties that changed instead of all the properties.

Required

This is the opposite of Partial. It makes all the properties of the input type required.

type PartialBlogPost = {
  title?: string;
  author?: string;
}

type BlogPost = Required<PartialBlogPost>;
/* same as {
  title: string;
  author: string;
} */

A use case for this one would be where portions of code need all members to be present but they can be optional somewhere else. In the place where they are required, you can explicitly use the required built-in utility type to ensure that the values have been initialized and you would not have to handle null checking in that code block.

Readonly

This constructs a type with all the properties of the input type set as read only. The properties of the returned type cannot be reassigned.

type BlogPost = {
  title: string;
  author: string;
}

type BlogPost = Readonly<PartialBlogPost>;
/* same as {
  readonly title: string;
  readonly author: string;
} */

The usage of this one is somewhat obvious, to freeze an object and prevent edits.

Pick

This is a bit more advanced utility type. It allows picking only specified keys from the input type.

type Point3D = {
  x: number,
  y: number,
  z: number,
};


type Point2D = Pick<Point3D, 'x' | 'y'>;
/* same as {
  x: number,
  y: number
} */

Omit

The opposite of Pick is Omit. It allows exclusion of properties that will not be needed.

type Point3D = {
  x: number,
  y: number,
  z: number,
};


type Point2D = Omit<Point3D, 'z'>;
/* same as {
  x: number,
  y: number
} */

Record

Given a set of properties specified by a bunch of keys, and corresponding property values, the resulting type is records of key-value pairs. So Record<Keys, Type> will provide a mechanism to create an interface by mapping all unit types specified in keys to the specified Type as their value’s type.

type BlogPost = Record<'title'| 'author', string>

/* same as {
  title: string;
  author: string;
} */

If all types have the same value, the Record version of declaration is a bit more concise and readable since it is obvious that all of them have the same type.

Extract

This extracts a type by including only the keys which are present in both given types.

type T0 = Extract<"a" | "b" | "c", "a" | "f">;
     // type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;  
     // type T1 = () => void

It can be thought of as an intersection of two types. It can be useful in cases where you want to find the common base of two types.

Exclude

This is the opposite of Extract. It excludes keys which are duplicate in given types.

type T0 = Exclude<"a" | "b" | "c", "a">;
     // type T0 = "b" | "c"

type T1 = Exclude<string | number | (() => void), Function>;
     // type T2 = string | number

NonNullable

This is an easy way to exclude null and undefined from your values in a type.

type T0 = NonNullable<string | number | undefined>;
     // type T0 = string | number

type T1 = NonNullable<string[] | null | undefined>;
     // type T1 = string[]

Conclusion

And these are the most commonly used utility types for transforming TypeScript types. There are a few others such as Parameters, ConstructorParameters, ReturnType, and InstanceType which exist but are not frequently used. You can check their usage here if you are interested..

If you have any questions about the usage of any of these types, drop a comment below.

Recent Articles

The Pomodoro Technique: Productivity hacks

The irony of time management productivity tips is that the people who need them the most do not realize that they do....

React fragments: What and Why

React fragments were a feature released in React 16.2. They have been around for a while now but have been a relatively...

Using Sub Resource Integrity to secure web applications

Sub Resource Integrity (SRI) is a security feature that can be used to validate that the resources that the browser is fetching...

Measuring JavaScript execution time

When it comes to performance of applications, measuring performance becomes important. For anything to be optimized, it must be measured first, optimized,...

The Beginner Syndrome

Someone recently reached out to me and asked how am I able to write consistently. Her exact words were: "how do you...

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Hi there! Want some more knowledge?

Think that the knowledge shared is helpful? You might want to give our mailing list a try. We'll send you 2-4 emails a month, right when new posts come out.

Hi there! Want some more knowledge?

Think that the knowledge shared is helpful? You might want to give our mailing list a try. We'll send you 2-4 emails a month, right when new posts come out.