How Do TypeScript Utility Types Work Behind The Scenes
Utility Types are built using TypeScript's own features and provide a powerful set of tools for working with types.
What are Utility Types?
Utility types can be considered as type functions that take one or more types as input and produce a new type as output. These types can be used to transform, combine, or manipulate other types in various ways. TypeScript provides several built-in utility types, as we looked at in the previous article, such as Partial, Required, Record, and so on. TypeScript also allows you to define your custom utility types.
How Does TypeScript Create Utility Types Behind The Scenes?
Utility types are just like a library of functions that are built inside TypeScript for you to use, ready and without thinking about the implementation or implementing them from scratch. Utility types are built using TypeScript's own features behind the scenes. By understanding how utility types are implemented behind the scenes, you can gain a deeper understanding of how TypeScript works and how to use it effectively in your projects. There are two key features of the language that make utility types possible let's look at them one by one.
Mapped Types
The first feature of TypeScript that actually makes utility types possible are the mapped types. We already know mapped types allow you to create new types based on existing types by applying a mapping function to each property of the original type. For example, the Partial utility type is defined as follows:
type Partial<T> = {
[P in keyof T]?: T[P];
};
This code defines a new type Partial<T>
that takes a type T
as input and produces a new type as output. The output type has all the same properties as the input type, but each property is optional (i.e., has a ?
suffix). This is achieved by using a mapped type that iterates over each property of T
and adds the ?
suffix to each property's type.
Conditional Types
Another important TypeScript feature that utility types use is conditional types. Conditional types allow you to create types that depend on some condition being true or false. For example, the Exclude utility type is defined as follows:
type Exclude<T, U> = T extends U ? never : T;
This code defines a new type Exclude<T, U>
that takes two types T
and U
as input and produces a new type as output. The output type contains all the properties of T
that are not assignable to U
. This is achieved by using a conditional type that checks whether each property of T
is assignable to U
using the extends keyword and, if so, returns the never type (which represents an impossible type). Otherwise, it returns the original property's type.
The Takeaway From This Article
At this moment, you might not understand what an Exclude<T, U>
is or what a Partial<T>
is, and you do not need to sweat about it as we are going to look at all the utility types that are offered by TypeScript one at a time in the next articles in this series on utility types. But this article intends to make you understand that all the utility types that we are going to talk about in the upcoming articles in this series are built using the features of TypeScript as a language that you already know. There is no secret sauce to utility types and how they work. They are just like a library of functions that are built inside TypeScript for you to use, ready and without thinking about the implementation or implementing them from scratch.