TypeScript is a powerful superset of JavaScript that introduces static typing, improving code quality and maintainability. One of the lesser-known yet incredibly useful features in TypeScript is utility types. These built-in types help manipulate and transform existing types to suit your needs, saving time and reducing boilerplate code.
In this post, we'll explore some of the most useful TypeScript utility types with practical examples.
What Are Utility Types?
Utility types are predefined types provided by TypeScript to manipulate and compose existing types. They enable developers to extract, transform, or extend types efficiently, reducing redundancy in type definitions.
1. Partial
The Partial utility makes all properties of a type optional.
Example
interface User {
id: number;
name: string;
email: string;
}
function updateUser(user: Partial) {
console.log("Updated user:", user);
}
updateUser({ name: "Alice" }); // Valid
updateUser({}); // Valid
2. Required
The Required utility makes all properties of a type mandatory.
Example:
interface User {
id?: number;
name?: string;
email?: string;
}
const createUser = (user: Required) => {
console.log("User created:", user);
};
// Error: Property 'id' is missing
createUser({ name: "Alice", email: "alice@example.com" });
3. Readonly
The Readonly utility makes all properties of a type immutable.
Example:
interface User {
id: number;
name: string;
}
const user: Readonly = { id: 1, name: "Alice" };
// Error: Cannot assign to 'name' because it is a read-only property
user.name = "Bob";
4. Pick
The Pick utility creates a new type by selecting specific properties from an existing type.
Example:
interface User {
id: number;
name: string;
email: string;
}
type UserPreview = Pick;
const preview: UserPreview = { id: 1, name: "Alice" };
// Error: 'email' does not exist on type 'UserPreview'
preview.email = "alice@example.com";
5. Omit
The Omit utility creates a new type by excluding specific properties from an existing type.
Example:
interface User {
id: number;
name: string;
email: string;
}
type UserWithoutEmail = Omit;
const user: UserWithoutEmail = { id: 1, name: "Alice" };
// Error: 'email' does not exist on type 'UserWithoutEmail'
user.email = "alice@example.com";
6. Record
The Record utility creates a type with keys of type K and values of type T.
Example:
type Role = "admin" | "user" | "guest";
const rolePermissions: Record = {
admin: ["read", "write", "delete"],
user: ["read", "write"],
guest: ["read"],
};
7. Exclude
The Exclude utility removes all types from T that are assignable to U.
Example:
type AllRoles = "admin" | "user" | "guest";
type NonGuestRoles = Exclude;
// Valid
const role: NonGuestRoles = "admin";
// Error: Type '"guest"' is not assignable
const invalidRole: NonGuestRoles = "guest";
8. Extract
The Extract utility selects only the types from T that are assignable to U.
Example:
type AllRoles = "admin" | "user" | "guest";
type OnlyGuestRole = Extract;
const guest: OnlyGuestRole = "guest";
// Error: Type '"admin"' is not assignable
const invalidRole: OnlyGuestRole = "admin";
9. NonNullable
The NonNullable utility removes null and undefined from a type.
Example:
type NullableString = string | null | undefined;
type NonNullString = NonNullable;
const name: NonNullString = "Alice";
// Error: Type 'null' is not assignable
const invalidName: NonNullString = null;
10. ReturnType
The ReturnType utility extracts the return type of a function.
Example:
function getUser() {
return { id: 1, name: "Alice" };
}
type User = ReturnType;
const user: User = { id: 1, name: "Alice" };
Conclusion
TypeScript utility types are indispensable tools that streamline type manipulation, making your code more concise and robust. By incorporating these utilities into your projects, you can handle complex type scenarios with ease.