Utility Types, in Typescript.
Guilherme Niches
Posted on December 20, 2022
In our day routine as software developer we usually needs to manipulate data and types for multiple purposes: to update a entry in database, to mapping a DTO (Data Transfer Object), to structure our return to the client and much more.
In Typescript, we have some resources that help us to handling with this Object Type Transformations called Utility Types.
Okay, but what's Object Type Transformations?
"Object Type Transformations refers to generating a modified type from an existing type or interface that represents an object." (Source: Refine.dev)
Cool, now we know about this Typescript resources and about Object Type Transformations. So, let's see some examples?
Note: Some utility Types described here are available only from Typescript 3.5 or upper.
Utility Types of Typescript
Typescript has a great diversity of types that can help with some common type manipulation, in this article we will see some of them.
Partial
- Changes all the properties in an object to be optional.
Syntax definition:
type Partial<T> = {[A in keyof T]?: T[A]};
Right, but what does the above example mean?
We know that T is the type passed to Partial, in the below example is IUser.
interface IUser {
uid: string;
name: string;
age: number;
};
So, keyof T in our example will be keyof IUser, in other words, keyof IUser it's equivalent of the keys of our object (in our example: "uid" | "name" | "age").
[A in "uid" | "name" | "age"]?
Hence, A must be any of the property keys of the IUser type.
['uid']? = IUser['uid']
['name']? = IUser['name']
['age']? = IUser['age']
All keys with the question mark (?) are to show that the property key is optional. Then, it's like the Partial generate other IUser interface (called PartialIUser in our example below) with all optional keys.
interface PartialIUser {
uid?: string;
name?: string;
age?: number;
};
Usecase Example
In the below example, the function updateUser would accept any user fields, ie, the User could be partially updated. And we can now use this function to update each field individually without needing the other ones to be set.
Omit
- Removes keys from an object type.
Syntax definition:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Maybe you noted, the Omit type is based on using other two utility types: Pick and Exclude (we are talk about Pick later).
Omit definition is equal to:
Pick<T, Exclude<keyof T, K>>
Using our IUser interface from previous example, again, T is our IUser and K is a string literal of keys that are removed from T, ie, our IUser.
Let's see a example.
Usecase Example
In the following example, we use Omit to remove the key "age" from our IUser interface and set the new type IUserWithoutAge to a new variable. In this variable, the property "age" is not available and can't be used.
Note that it's possible omit multiple keys from an object, passing a union of string literals in K:
type IUserWithoutAgeAndName = Omit<IUser, "age" | "name">;
Pick
- "Keeps" only the specified keys.
Syntax definition:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Pick will generate a new object. So, it's not possible to have a primitive type (string or number, for example) as the new type.
Breaking down the syntax definition and using our IUser interface as example we have the following:
T[P] is a way to access the type of a property. So, if we want access the "age" property from our IUser interface we can do like this: IUser['age'].
Again, keyof T are the keys of our type, in this example IUser interface and the keys "uid", "name" and "age".
And we have [P in K] which means that property P needs be one of the keys extracted from K. Then, P could be either "uid", "name" or "age".
Usecase Example
In the example above, we picked the key "age", so, the variable that has the type IUserWithOnlyAge has only the key "age" to be setted, the others properties "uid" and "name" hasn't available.
Readonly
- Turn all properties readonly.
Syntax definition:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Understanding the syntax definition:
Like the previous examples, T[P] is a way to access the type of a property and keyof T are the keys of our type.
The readonly prefix is a keyword of typescript that makes a property as read-only in the class, type or interface.
So, basically, for each property of our type we are turning this property readonly.
Usecase Example
The above example show us that try reassign the variable newUser with has IUserReadonly type will cause a compilation error.
Concluding
In this article we just scratch the surface of utility types of Typescript. Has a lot more utility types that can be very useful for different usecases and you can discovery it in Typescript documentation.
Utility types help us to handling with transform and manipulate data and can improve our code turning it more clean, should be used wisely.
Let your comment if you already used utility types.
Reviews, suggestions and/or contributions to this article are encouraged and welcome.
References
Posted on December 20, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.