TypeScript provides a set of utility types that help transform or manipulate types more flexibly and powerfully. These types are useful for building more dynamic and reusable codebases. These utility types are built-in and can be used to create new types based on existing ones. In this article, we'll explore some of the advanced TypeScript utility types and how they can be used to improve your code.
1. Partial<Type>
The Partial utility type
makes all properties of the given type T
optional. It is useful when you want to work with objects
where only some properties may be provided, or when you're working with optional values in forms or updates.
interface User {
id: number;
name: string;
email: string;
}
// All properties are now optional
type PartialUser = Partial<User>;
function updateUser(id: number, user: PartialUser) {
// Update user properties
}
updateUser(id: 1, { name: "Aasim" });
2. Required<Type>
The Required utility type
does the opposite
of Partial
. It makes all properties of the given type T required
, ensuring that all properties must be present.
interface User {
id: number;
name?: string; // Optional property
email?: string; // Optional property
}
// All properties are now required
type RequiredUser = Required<User>;
const user: RequiredUser = {
id: 1,
name: "Aasim",
email: "contact@bhataasim.com",
};
3. Readonly<Type>
The Readonly utility type
makes all properties of the given type T readonly
, meaning they cannot be modified once they are created. It is useful when you want to prevent accidental changes to objects.
interface User {
id: number;
name: string;
email: string;
}
// All properties are now readonly
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = {
id: 1,
name: "Aasim",
email: "contact@bhataasim.com",
};
// Error: Cannot assign to 'name' because it is a read-only property
user.name = "Bhat Aasim";
4. Pick<Type, Keys>
The Pick utility type
allows you to create a new type by selecting only a subset of properties from the given type T
. It is useful when you want to create a new type with only specific properties.
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Select only 'id' and 'name' properties
type UserBasicInfo = Pick<User, "id" | "name">;
const user: UserBasicInfo = {
id: 1,
name: "Aasim",
};
5. Omit<Type, Keys>
The Omit utility type
is the opposite of Pick
. It allows you to create a new type by excluding a subset of properties from the given type T
. It is useful when you want to create a new type without specific properties.
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Exclude 'id' and 'email' properties
type UserPersonalInfo = Omit<User, "id" | "email">;
const user: UserPersonalInfo = {
name: "Aasim",
age: 21,
};
6. Exclude<Type, ExcludedUnion>
The Exclude utility type
removes all types from Type
that are assignable to ExcludedUnion
. It is useful when you want to exclude certain types from a union.
type Status = "active" | "inactive" | "pending";
type ActiveStatus = Exclude<Status, "pending">;
const status: ActiveStatus = "active";
// Error: Type '"pending"' is not assignable to type 'ActiveStatus'.
const status: ActiveStatus = "pending";
7. Extract<Type, Union>
Extract
does the opposite of Exclude
. It constructs a type by extracting all types from Type
that are assignable to Union
. It is useful when you want to extract certain types from a union.
type Status = "active" | "inactive" | "pending";
type ActiveStatus = Extract<Status, 'active' | 'inactive'>;
const status: ActiveStatus = "active";
// Error: Type '"pending"' is not assignable to type 'ActiveStatus'.
const status: ActiveStatus = "pending";
8. Record<Keys, Type>
The Record utility type
constructs an object type whose property keys are Keys
and whose property values are Type
. It is useful when you want to create an object type with specific keys and values. It is commonly used to create dictionaries or maps in TypeScript.
// example 1
type Roles = "admin" | "user" | "guest";
type UserRoles = Record<Roles, boolean>;
const roles: UserRoles = {
admin: true,
user: true,
guest: false,
};
// example 2
type PageInfo = {
title: string,
url: string,
};
type Page = 'home' | 'about' | 'contact';
const pages: Record<Page, PageInfo> = {
home: { title: 'Home', url: '/' },
about: { title: 'About', url: '/about' },
contact: { title: 'Contact', url: '/contact' },
};
9. NonNullable<Type>
The NonNullable utility type
removes null
and undefined
from the given type T
. It is useful when you want to ensure that a value is not null
or undefined
.
type User = {
id: number;
name: string | null;
};
// Remove null and undefined
type NonNullableUser = NonNullable<User>;
const user1: NonNullableUser = {
id: 1,
name: "Aasim",
};
const user2: NonNullableUser = {
id: 2,
name: null, // Error: Type 'null' is not assignable to type 'string'.
};
10. ReturnType<Type>
The ReturnType utility type
extracts the return type of a function type. It is useful when you want to get the return type of a function dynamically.
function getUser() {
return { id: 1, name: "Aasim" };
}
type User = ReturnType<typeof getUser>;
const user: User = {
id: 1,
name: "Aasim",
};
11. Parameters<Type>
The Parameters utility type
extracts the parameter types of a function type as a tuple. It is useful when you want to get the parameter types of a function dynamically.
function loginUser(username: string, password: string) {
// Login logic
}
type LoginParams = Parameters<typeof loginUser>;
function authenticateUser(...params: LoginParams) {
// Authenticate user
}
authenticateUser("admin", "password");
authenticateUser("user", 123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.
Conclusion
These are some of the advanced TypeScript utility types that can help you write more robust and flexible code. By leveraging these utility types, you can create more dynamic and reusable types in your TypeScript projects.
Checkout the Official TypeScript Documentation for more utility types: TypeScript Utility Types
I hope you found this article helpful. If you have any questions or feedback, feel free to reach out. Happy coding! 🚀
Top comments (4)
I would not say there is something 'advanced' about these. It should be in every experienced Typescript dev toolkit. I have seen too many people 'knowing' Typescript when they put any as type for anything more complex that flat object or something can't be bothered to Google 'how to'
I enjoyed that read, it made me realise I probably only use Partial, Record and ReturnType, I should look at using the others on occasion. 👍🏼
Thanks
It is great to see all at one place. Thank you @bhataasim .
But, i think the example use gave for NonNullable type is wrong. Can you check once?
Usage:
Correct me if am wrong.