TypeScript Utility Types

Featured on Hashnode

Introduction

In this article I'll be going over TypeScript utility types, explain their importance and also include some useful examples and use cases.

Utility types are built-in types that allow you to transform existing types into new modified types.

Untitled Diagram.drawio (3).png

Say we have type A and we'd like to construct a new type B that may inherit, modify or exclude properties from type A.

We achieve this by using a utility type i.e type B = UtilityType<A, options> where UtilityType represents the type to be used and options describe the type of operation to perform e.g inherit, modify or exclude etc.

Utility types aren't supported by primitives except strings which offer Intrinsic String Manipulation Types to manipulate strings.
Utility types are usually used with compound types such as Interfaces and Union Types.

Before you get started

The TypeScript playground is an excellent place to experiment with Utility Types. It's highly recommended to practice what you learn in order to solidify your understanding of the topic.

Union Types

A union type is a type formed from two or more other types, representing values that may be any one of those types. We refer to each of these types as the union’s members.

Union types basically combine two or more types.

Unions support the following utility types:

This example to demonstrate how to use utility types with unions.

Caveat: Union types are case-sensitive.

type ActivityType =
  "Swimming"
  | "Reading"
  | "Coding"
  | "Dancing"
  | "Jogging";

1 -Extract

Constructs a type by extracting from Type all union members that are assignable to Union.

Syntax Extract<Type, Union>

Where Type is the initial type and Union represents the entries we'd like to extract.

Demo

type CognitiveActivity = Extract<ActivityType, "Reading" | "Coding">;
// transformed type: "Reading" | "Coding"
const activity: CognitiveActivity = "Reading"; 
console.log(activity);

2 - Exclude

Constructs a type by excluding from UnionType all union members that are assignable to ExcludedMembers.

Syntax Exclude<UnionType, ExcludedMembers>

Where UnionType is the initial type and ExcludedMembers represents the entries we'd like to exclude.

Demo

type PhysicalActivity = Exclude<ActivityType, "Reading" | "Coding">;
 // transformed type: "Swimming" | "Dancing" | "Exercise";
const activity: PhysicalActivity = "Swimming";
console.log(activity);

Type Aliases and Interfaces

  • Type Aliases: A type alias is a name for any type.

  • Interfaces: An interface declaration is another way to name an object type.

This example to demonstrate how to use utility types with type aliases and interfaces.

Intersection types are types that combine properties from two or more types. They're usually used in conjunction with the following utility types.

The general syntax is

type A = {
   [key: string]: string;
};
type B = A & {
   bKey: string;
}; // includes all the properties in A and B
type ElectronicDevice = {
  name: string;
  USBPorts: USBPORT[];
  screenDimensions: Dimension;
  buttons: Buttons[];
  wifiConnectivity: boolean;
  manufacturer: string;
  memory: number;
}

This type represents the properties of an electronic device. Electronic devices come in a variety of shapes and sizes but they share a lot of core features such as buttons, memory, ports etc.

You can test the types in this example on this TypeScript playground.

1 - Pick

Syntax Pick<Type, Keys>

Constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type.

Demo

type Calculator = Pick<ElectronicDevice, "name"  | "buttons"> & {
  type: CalculatorType;
  // own types
}

2 - Record

Syntax Record<Keys, Type>

Constructs an object type whose property keys are Keys and whose property values are Type. This utility can be used to map the properties of a type to another type.

Demo

type DeviceType = "Analogue" | "Digital";

type SmartPhone = Record<DeviceType, ElectronicDevice> & {
  type: CalculatorType;
  // own types
}

const blackberry: SmartPhone = {
    Digital: {

    }
}

3 - Omit

Syntax Omit<Type, Keys>

Constructs a type by picking all properties from Type and then removing Keys (string literal or union of string literals).

Demo

type StopWatch = Omit<ElectronicDevice, "wifiConnectivity" | "USBPorts">;

4 - ReturnType

Syntax ReturnType<Type>

Constructs a type consisting of the return type of function Type.

Demo

const getUserByActivity = async (type: string) => {
  const response = await apiCall(url, params); // fake api call
  return response.data;
}
type NewUser = ReturnType<typeof getUserByActivity>
const user: NewUser = getUserByActivity(activity.type);

References: