When working with TypeScript, you often deal with complex object types. But what if you need to work with just a subset of an object's properties? Or pick specific properties? TypeScript provides several built-in utility types to handle these scenarios efficiently.

Understanding and using these utilities effectively can make your code more robust and maintainable.

Partial<T> and Required<T>

Partial<T> takes an object type T and makes all its properties optional. This is useful when dealing with updates, default values, or step-by-step construction of objects.

interface User {
  id: number;
  name: string;
  email: string;
}

const updateUser = (id: number, userUpdates: Partial<User>) => {
  // Merge updates with existing user data
};

Partial<T> is useful for making flexible update functions.

The opposite of Partial<T>, the Required<T> utility type ensures that all properties of T are mandatory.

type RequiredUser = Required<User>;

Required<T> ensures all properties are provided.

Readonly<T>

Readonly<T> makes all properties of T immutable, preventing modification after initialization.

const user: Readonly<User> = { id: 1, name: "Alice", email: "alice@example.com" };
// user.name = "Bob"; // Error: Cannot assign to 'name' because it is a read-only property.

Readonly<T> helps in maintaining data integrity.

Pick<T, K> and Omit<T, K>

Pick<T, K> allows you to create a new type by selecting specific properties from T.

type UserPreview = Pick<User, "id" | "name">;

Omit<T, K> is the inverse of Pick<T, K>, creating a new type by removing specified properties from T.

type UserWithoutEmail = Omit<User, "email">;

Pick<T, K> and Omit<T, K> let you shape types as needed.

There are more utility types available in TypeScript. You can find the complete list here.