11 Awesome TypeScript Utility Types You Should Know
Kai
Posted on December 21, 2020
This post was originally published at kais.blog.
Let's move your learning forward together! Follow me on Twitter for your daily dose of developer tips. Thanks for reading my content!
My last two posts have already shown you 14 Awesome JavaScript Array Tips You Should Know About and 10 Awesome JavaScript String Tips You Might Not Know About. However, I usually write code in TypeScript. There's also much to learn. So today, I'd like to show you 11 awesome TypeScript utility types. With these, constructing new types becomes a breeze.
- Pick<Type, Keys>
- Omit<Type, Keys>
- Readonly<Type>
- Partial<Type>
- Required<Type>
- Record<Keys, Type>
- Parameters<Type>
- ReturnType<Type>
- Extract<Type, Union>
- Exclude<Type, ExcludedUnion>
- NonNullable<Type>
You don't have to do anything special. All utility types are globally available by default.
Pick<Type, Keys>
With Pick
you can pick a set of Keys
from the given Type
. The example shows a signup
function taking a user
as first parameter. The parameter type is constructed by picking the email
and password
property from the User
type. This way, you won't need to pass an id
for signing up a new user.
interface User {
id: string;
email: string;
password: string;
}
function signup(user: Pick<User, "email" | "password">): User {
//
}
signup({
email: "kai@example.com",
password: "secret",
});
Omit<Type, Keys>
In contrast to Pick
, you can use Omit
to remove a set of Keys
from your Type
. The example is similar to the previous. In this case, the signup
function's parameter user
has all properties from the User
type minus the id
property.
interface User {
id: string;
email: string;
password: string;
}
function signup(user: Omit<User, "id">): User {
//
}
signup({
email: "kai@example.com",
password: "secret",
});
Readonly<Type>
Sometimes you want to prevent that an object's properties are reassigned. This is doable with the Readonly
utility type. The constructed type will have all properties set to read-only. Thus, you can't reassign any property of that type. In the following example, we'll create a new type by using Readonly
and the User
type. We can't reassign the password
property here, because it's read-only now.
interface User {
id: string;
email: string;
password: string;
}
const user: Readonly<User> = {
id: "d70989c8-c135-4825-a18c-a62ddf9ae1d5",
email: "kai@example.com",
password: "secret",
};
user.password = "correcthorsebatterystaple";
// ERROR: Cannot assign to 'password' because it is a read-only property.
Partial<Type>
Using Partial
you can construct a type with all properties from Type
set to optional. For example, the updateUser
function allows you to update a User
. The second parameter expects the fields to update. You can use Partial
with the User
type here, so that fields
is any combination of fields from the User
type.
interface User {
id: string;
email: string;
password: string;
}
function updateUser(user: User, fields: Partial<User>): User {
//
}
const user: User = {
id: "d70989c8-c135-4825-a18c-a62ddf9ae1d5",
email: "kai@example.com",
password: "secret",
};
updateUser(user, { password: "correcthorsebatterystaple" });
Required<Type>
Required
is the opposite of Partial
. You can use it to construct a type with all properties from Type
set to required. The following example has a User
type with an optional avatar
property. However, our variable userWithAvatar
requires all properties to be present. Thus, an error occurs.
interface User {
id: string;
email: string;
password: string;
avatar?: string;
}
const userWithAvatar: Required<User> = {
id: "d70989c8-c135-4825-a18c-a62ddf9ae1d5",
email: "kai@example.com",
password: "secret",
};
// ERROR: Property 'avatar' is missing in type '{ id: string; email: string; password: string; }' but required in type 'Required<User>'.
Record<Keys, Type>
With the Record
utility type, you can easily construct a new type with Keys
as keys and Type
as values. In this example, each User
has a role. We want to describe an object that groups userA
and userB
by their respective role. Using Record
, we can tell the TypeScript compiler that the object has a string
s as keys and an array of User
s as values. Besides, to be more explicit, we could have used User["role"]
instead of string
for the keys.
interface User {
id: string;
email: string;
password: string;
role: string;
}
const userA: User = {
id: "d70989c8-c135-4825-a18c-a62ddf9ae1d5",
email: "kai@example.com",
password: "secret",
role: "administrator",
};
const userB: User = {
id: "c0e26c7e-9787-4d56-81b4-4440759e251c",
email: "katharina@example.com",
password: "correcthorsebatterystaple",
role: "moderator",
};
const usersGroupedByRole: Record<string, User[]> = {
administrator: [userA],
moderator: [userB],
};
Parameters<Type>
Use Parameters
to extract a function's parameters. This will construct a tuple type with the parameters. Let's say you'd like to initialize a variable that holds parameters for a signup
function. With the help of Parameters
you can extract the signup
function's parameters and create a tuple type. Then, you can use the parameters
whenever you want.
interface User {
id: string;
email: string;
password: string;
}
function signup(email: string, password: string): User {
//
}
type SignupParameters = Parameters<typeof signup>;
// = [email: string, password: string]
const parameters: SignupParameters = ["kai@example.com", "secret"];
signup(...parameters);
ReturnType<Type>
The utility type ReturnType
helps by extracting the return type of a function. Look at the following example. We want our ValidationResult
type to be constructed by looking at the return type of the validate
function. Here, it's pretty simple. You could have used boolean
directly instead. However, sometimes it's nice to be able to extract a function's return type.
interface User {
id: string;
email: string;
password: string;
}
function validate(user: User): boolean {
//
}
type ValidationResult = ReturnType<typeof validate>;
// = boolean
Extract<Type, Union>
Sometimes, you'd like to extract types from an union of types. For that, you can use the Extract
utility type. Every union member from Type
that is assignable to the Union
is kept. In the following examples, we'll have unions of strings. There, we extract a part of it for our types TypeA
and TypeB
. For TypeC
we extract each union member that is assignable to Function
.
type TypeA = Extract<"apple" | "banana" | "cherry", "apple">;
// = "apple"
type TypeB = Extract<"apple" | "banana" | "cherry", "apple" | "banana">;
// = "apple" | "banana"
type TypeC = Extract<string | (() => string), Function>;
// = () => string
Exclude<Type, ExcludedUnion>
The Exclude
utility type is the opposite of the Extract
utility type. This time, it removes all union members from Type
that are assignable to the ExcludedUnion
. Similar to the previous examples, we have unions of strings here. In contrast to last time, we are removing union members instead of keeping them.
type TypeA = Exclude<"apple" | "banana" | "cherry", "apple">;
// = "banana" | "cherry"
type TypeB = Exclude<"apple" | "banana" | "cherry", "apple" | "banana">;
// = "cherry"
type TypeC = Exclude<string | (() => string), Function>;
// = string
NonNullable<Type>
NonNullable
works similar to the Exclude
utility type. It excludes null
and undefined
from the given Type
. Similar to the previous two examples, we construct the new types TypeA
and TypeB
by removing union members from a given Type
. Here, null
and/or undefined
are removed.
type TypeA = NonNullable<"apple" | null>;
// = "apple"
type TypeB = NonNullable<"apple" | null | undefined>;
// = "apple"
Conclusion
This was a quick overview of some of the most useful utility types. They can be used for a wide range of things. I use them in almost every project. However, there are more utility types. Check out the official documentation to learn more! Besides, you can find even more such types. For example, the type-fest
package adds many essential types to your project.
Thanks a lot for reading this post. Please consider sharing it with your friends and colleagues. See you soon!
Let's move your learning forward together! Follow me on Twitter for your daily dose of developer tips. Thanks for reading my content!
This post was originally published at kais.blog.
Posted on December 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 18, 2022