Taking a look at must-know utility types provided by TypeScript

In this lesson, we are going to learn about the generic types provided by TypeScript out of the box. These types are helpful to construct new types from existing types with some behavioral change.

TypeScript provides some standard types globally that act as utilities to convert existing types into new types with some modified behavior. These types are defined in the standard library of the TypeScript installation, hence you do not need to import them in your program. They are always available in your program and you can start using them right away.

In the Type System lesson, we learned about the keyof keyword to create a union type from the field names of an interface. Similarly, we learned typeof keyword to create an interface type from a plain object.

Similarly, these utility types appear in the type annotation which takes existing types as their input and returns an output type that we can use as a type for a value. I am going to cover important ones here, but you can follow this documentation link to know more about them.

💡 Types mentioned below are generic types. We are going to learn about generic types in the Generics lesson (coming soon). In this lesson, you will also learn how to create custom utility types on your own.


Partial and Required

The Partial<Type> utility type returns an interface by making all the fields of the Type optional. Normally, the Type here is an interface or a class (because call defines an implicit interface).

In the above example, the person argument of the sayHello function will accept a value that is a subset of Person interface. Technically, the type returned by the Partial<Person> annotation is the following.

interface PersonPartial {
    firstName?: string;
    lastName?: string;
}

TypeScript provides the Required utility type that does what Partial does but exactly the opposite. The Required<Type> utility type returns an interface by making all the fields of the Type required.

So the Required<PersonPartial> type annotation will return the following interface. Nothing amazing happened, just that all the ? are gone.

interface PersonRequired {
  firstName: string;
  lastName: string;
}

💡 You can also use the typeof keyword in the Partial type. For example, Partial<typeof person> would return a type with all the properties of person object optional. Same goes for the Required type.

Readonly

Similar to the Partial type, Readonly<Type> utility type returns an interface by making all the fields of the Type readonly.

In the Data Immutability lesson (coming soon), we learned that the readonly keyword makes a field of an interface immutable. Any action to override the value of a readonly field will result in a compilation error.

In the above example, the changeName function takes an argument of type Person but with all the fields of Person marked readonly. Hence any attempts to change a field value would be illegal.

interface PersonReadonly {
  readonly firstName: string;
  readonly lastName: string;
}

Record

In the Type System lesson, we learned about the keyof keyword to create a union type from the field names of an interface. So the type annotation keyof Person would return "firstName" | "lastName" which is union type of string unit types made from the field names of Person.

The Record<U,T> utility type provides a mechanism to create an interface by mapping all the unit types in the union U as keys with the type T as value.

In the above example, we have a union type keys which is the union of firstName and lastName literal (unit) types. The Record<keys, string> type returns an interface that looks something like this.

interface Person {
  firstName: string;
  lastName: string;
}

Pick and Omit

You can create a new interface by picking only certain fields of an existing interface using the Pick<I,U> utility type. Here, the I is an existing interface and U is a single unit type or a union of unit types. Hence this expression returns an interface by picking fields in I that are listed in U.

The Omit<I,U> utility type provides a similar behavior but instead of picking fields from I that are listed in U, it returns the interface I except for the fields that are listed in U. Here, U can be a unit type or a union of unit types.

In the above example, Person and Human types are semantically the same since they contain firstName and lastName fields described in the Student interface. The only difference is how they were created.

Extract and Exclude

The Extract and Exclude utility types provide similar behavior of Pick and Omit respectively, however, these types operate on union types.

The Extract<U,T> type returns a union type by picking types from U that are a subset of T. For example, if U contains the type 'hello' | 'world' | 3, then it would return 'hello' | 'world' if T is string since 'hello' and 'world' literal types are subset of string type.

Similarly, Exclude<U,T> type returns a union type by omitting types that are a subset of T. Let’s see some variations of U and T and their final results.

In the above example, the greenSet would only have the type 'green' since only 'green' is a subset of type 'green'. The safeSet would have 'green' and 'blue' because these types are a subset of 'green' | 'blue' union.

The stringSet and stringSet2 would have all the string literal types because a string literal (unit) type is a subset of string type (collective).

The Function type is a class in JavaScript that constructs every single function. Hence funcSet would only contain functions that are in the colorSet union.

The literalSet contains all the types in the colorSet except for the function types. This is because any value that is not a primitive data type is a subset of the object. Since only the function in the set is a type of object, Exclude will return a union with the function (object_ more precisely_) type removed.

NonNullable

The NonNullable<U> utility type returns a union U by excluding null and undefined type. This is similar to using Exclude<U,T> type where T being null | undefined, however, this is much cuter.

Other Important Utility Types

  1. Parameters<F>: This utility type returns a tuple type that contains the types of the parameters function F has, in order. This can be useful to construct a tuple (array) with values that can be passed to the function F.
  2. ReturnType<F>: This type returns the return type of the function F. The return type of a function that returns never is any.
#typescript #utility-types