When working with TypeScript, one of the most powerful tools in our developer toolkit is utility types. These built-in helpers let us transform types, enforce constraints, and write more flexible, safer code.
In this blog, we’ll break down the most common TypeScript utility types with simple, real-world examples, making them easier to understand and apply in our own codebase.
Common TypeScript Utility Types
1. Partial<T>
Makes all properties in a type optional.
Use case: When updating only part of an object (e.g. a PATCH API).
type User = {
name: string;
email: string;
};
const updateUser = (id: string, data: Partial<User>) => {
// data can contain name, email, or both (or neither)
};
updateUser("1", { email: "new@example.com" });
2. Required<T>
Makes all properties in a type required, even if they were optional before.
type User = {
name?: string;
email?: string;
};
const createUser = (user: Required<User>) => {
// name and email must be provided
};
createUser({ name: "John", email: "john@example.com" });
3. Readonly<T>
Makes all properties read-only — they can’t be changed after being set.
type User = {
name: string;
};
const user: Readonly<User> = { name: "Alice" };
// user.name = "Bob"; ❌ Error: Cannot assign to 'name'
4. Record<K, T>
Creates an object type with keys of type K
and values of type T
.
Use case: Creating a mapping object.
type Role = "admin" | "user" | "guest";
const permissions: Record<Role, string[]> = {
admin: ["create", "read", "update", "delete"],
user: ["read"],
guest: [],
};
5. Pick<T, K>
Creates a new type by picking a subset of properties from T
.
type User = {
id: number;
name: string;
email: string;
};
type PublicUser = Pick<User, "id" | "name">;
const user: PublicUser = {
id: 1,
name: "John",
};
6. Omit<T, K>
Creates a type by excluding one or more properties from T
.
type User = {
id: number;
name: string;
email: string;
};
type UserWithoutEmail = Omit<User, "email">;
const user: UserWithoutEmail = {
id: 1,
name: "John",
};
7. Exclude<T, U>
Removes from T
the types that are assignable to U
.
type Roles = "admin" | "user" | "guest";
type LimitedRoles = Exclude<Roles, "guest">;
// "admin" | "user"
8. Extract<T, U>
Keeps from T
only the types that are assignable to U
.
type Roles = "admin" | "user" | "guest";
type StaffRoles = Extract<Roles, "admin" | "user">;
// "admin" | "user"
9. NonNullable<T>
Removes null
and undefined
from a type.
type MaybeUser = string | null | undefined;
type User = NonNullable<MaybeUser>; // string
10. ReturnType<T>
Extracts the return type of a function.
const getUser = () => {
return { id: 1, name: "Alice" };
};
type User = ReturnType<typeof getUser>; // { id: number; name: string }
11. Parameters<T>
Extracts the parameter types of a function into a tuple.
function greet(name: string, age: number) {
return `Hello ${name}, age ${age}`;
}
type GreetParams = Parameters<typeof greet>; // [string, number]
12. ConstructorParameters<T>
Extracts the types of a class constructor's parameters.
class Person {
constructor(public name: string, public age: number) {}
}
type PersonArgs = ConstructorParameters<typeof Person>; // [string, number]
13. InstanceType<T>
Gets the instance type of a class constructor.
class Car {
wheels = 4;
}
type CarInstance = InstanceType<typeof Car>; // Car
14. Awaited<T>
Unwraps the type of a Promise, recursively if needed.
async function fetchUser(): Promise<{ name: string }> {
return { name: "Alice" };
}
type User = Awaited<ReturnType<typeof fetchUser>>; // { name: string }
15. NoInfer<T>
Prevents TypeScript from inferring a type too early. Useful in generic helpers. It is not built-in but often used via workaround.
type NoInfer<T> = [T][T extends any ? 0 : never];
function useDefault<T>(value: T, fallback: NoInfer<T>): T {
return value !== undefined ? value : fallback;
}
Wrapping Up
TypeScript’s utility types are secret weapon to write clean, reusable, and safe type definitions without unnecessary duplication. By using them, we can simplify code and boost our productivity.
Top comments (0)