# TypeScript / How some utility types are implemented

Posted On 03.30.2022

TypeScript provides several utility types to help us manipulate types easier, like: Partial<T>, Required<T>, Pick<T, Keys>,…

Internally, these types are implemented in the src/lib/es5.d.ts, at the type level. And their implementation details are very interesting.

## Prerequisites: Mapped Type

A mapped type is a generic type that iterates through the keys of another type, to create a new type. For example:

type AllBoolean<Type> = {
[Key in keyof Type]: boolean
}


The keyof keyword returns a union of all the keys inside a type, we then use the index signature syntax to iterate through these keys, map them with the Boolean type, the end result is a new type containing all the keys of Type, with the type Boolean.

Later on, we can use this AllBoolean type like this:

type Foo = {
name: string;
age: number;
}

type BoolFoo = AllBoolean<Foo>;
//    ^? {
//         name: boolean,
//         age: boolean,
//       }


## Types Impelementations

Now, let’s go over some of the utility-type implementations. Most of them are created using mapped type.

### The Partial<T> Type

The Partial<T> type takes a type T and makes all of the fields in T optional. Here’s how Partial<T> is implemented:

type Partial<T> = {
[P in keyof T]?: T[P];
};


It mapped all the field P of T into the type T[P] (which means, get the type of P as defined in T). But also added a ? modifier for each of the fields.

The ? modifier indicates that the field is optional.

### The Required<T> Type

Opposite from Partial<T> is Required<T>, which makes all the fields of type T become required. It was implemented as:

type Required<T> = {
[P in keyof T]-?: T[P];
};


The way it works is almost identical to how Partial<T> works, except this time, it removes the optional ? modifier with the -? operator.

When the optional modifier is removed from a field, that field becomes required.

The Readonly<T> type makes all the properties in T become read-only, it does so by annotating each field with the readonly keyword while mapping:

type Readonly<T> = {
readonly [P in keyof T]: T[P];
};


### The Pick<T, K> Type

Now, this one has much more to talk about. The Pick<T, K> type takes a type T and returns a new type that only has the fields defined in the union K. It is implemented as:

type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};


First, for the type parameters, the list of keys K is defined as:

K extends keyof T


This means, K is a union that only contains the fields inside the type T, so we cannot pass some arbitrary values in here, because it would cause a problem during the mapping phase.

Next, we will iterate through the keys in K and map to the corresponding field found in the type T.

### The Exclude<T, U> Type

The Exclude<T, U> type is used to make sure T will never be any type that is assignable to U. It does so by checking if the type T is extendable to U, returns a never type, otherwise, return the type T itself:

type Exclude<T, U> = T extends U ? never : T;


This means, U can be anything, a primitive type or a union. This type is very interesting because it can be used as a building block for other types, for example: Omit<T, K>.

### The Omit<T, K> Type

As the name implies, the Omit<T, K> is a reversed version of Pick<T, K>, it removes all the fields defined in K from the type T, with the help of Exclude<T, K>:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;


This type can be interpreted step by step:

• First, create a type U that is a union of every field in type T, which is not assignable to anything in type K:

type U = Exclude<keyof T, K>


By doing this, we are able to remove all the fields in T that is assignable to K.

• Finally, since U only contains the fields that are not found in K, we can pick them:

type Result = Pick<T, U>


### The ReturnType<T> Type

OK, I know you are starting to get a headache already. This is the last one, I promise. The ReturnType<T> type is used to get the return type of a function type, this one is interesting:

type ReturnType<T extends (...args: any) => any>
= T extends (...args: any) => infer R ? R : any;


Whoa, there’s a lot going on here.

First, the input type parameter of this type means, we take a type T that has the shape of a function:

T extends (...args: any) => any


In the implementation, we use a conditional type to check if the return type of function type T can be inferred or not, and call the inferred type here is R:

T extends (...args: any) => infer R


If it is, we return the inferred type R, otherwise, it can be anything, hence, any.