This guide explains TypeScript utility types that manipulate object properties, with real-world examples.
#1 - Omit
Description: Omit<T, K> creates a new type from T excluding keys K. Useful for POST/PUT requests where some properties (like id) are generated by the server.
interface IUser {
id: number;
name: string;
email: string;
}
// ❌ Manually copy type without id
interface INewUser {
name: string;
email: string;
}
// ✅ Use Omit
type INewUserGeneric = Omit<IUser, 'id'>;
// Example: creating a new user
const newUser: INewUserGeneric = { name: 'Jhon', email: 'jhon@example.com' };
#2 - Pick
Description: Pick<T, K> creates a type by selecting specific keys K from T. Useful for partial responses or form data.
interface IUser {
id: number;
name: string;
email: string;
}
// ❌ Manual selection
interface IUserNameOnly {
name: string;
}
// ✅ Use Pick
type IUserNamePick = Pick<IUser, 'name'>;
const userName: IUserNamePick = { name: 'Mery' };
#3 - Partial
Description: Partial<T> makes all properties optional. Useful for PATCH requests or optional fields in forms.
interface IUser {
id: number;
name: string;
email: string;
}
// ❌ Manual optional
interface IUserPatchManual {
id?: number;
name?: string;
email?: string;
}
// ✅ Use Partial
type IUserPatch = Partial<IUser>;
const updateUser: IUserPatch = { name: 'Jhon Updated' };
#4 - Required
Description: Required<T> converts all optional properties to required. Useful for ensuring full data after vjhondation.
interface IUserPatch {
name?: string;
email?: string;
}
// ❌ Manually require properties
interface IUserFull {
name: string;
email: string;
}
// ✅ Use Required
type IUserFullRequired = Required<IUserPatch>;
const fullUser: IUserFullRequired = { name: 'Jhon', email: 'jhon@example.com' };
#5 - Readonly
Description: Readonly<T> makes all properties immutable. Useful for state management or constants.
interface IUser {
id: number;
name: string;
}
// ❌ Mutable object
let user = { id: 1, name: 'Jhon' };
user.id = 2; // allowed ❌
// ✅ Readonly
const readonlyUser: Readonly<IUser> = { id: 1, name: 'Jhon' };
// readonlyUser.id = 2 // ❌ Error: cannot assign to 'id'
#6 - Record
Description: Record<K, T> creates an object type with keys K and values T. Useful for lookup tables or maps.
interface IUser {
id: number;
name: string;
}
// ❌ Manual object type
const userMapManual: { [key: string]: IUser } = {
u1: { id: 1, name: 'Jhon' },
u2: { id: 2, name: 'Mery' },
};
// ✅ Use Record
type UserMap = Record<string, IUser>;
const userMap: UserMap = {
u1: { id: 1, name: 'Jhon' },
u2: { id: 2, name: 'Mery' },
};
#7 - Exclude & Extract
Description: Exclude<T, U> removes types, Extract<T, U> keeps only specified types. Useful for union type filtering.
type Status = 'active' | 'inactive' | 'pending';
// ❌ Manual union exclusion
type ActiveOnlyManual = 'active';
// ✅ Exclude
type ActiveOnly = Exclude<Status, 'inactive' | 'pending'>;
// ✅ Extract
type PendingOnly = Extract<Status, 'pending'>;
const userStatus: ActiveOnly = 'active';
const pending: PendingOnly = 'pending';
#8 - NonNullable
Description: NonNullable<T> removes null and undefined from a type. Useful for sanitized API responses.
type NullableString = string | null | undefined;
// ❌ Manual exclusion
type NotNullManual = string;
// ✅ NonNullable
type NotNull = NonNullable<NullableString>;
const name: NotNull = 'Jhon';
#9 - ReturnType & Parameters
Description: Get the return type or parameter types of a function. Useful for DRY typing.
function getUser() {
return { id: 1, name: 'Jhon' };
}
// ❌ Manual type
interface IUserManual {
id: number;
name: string;
}
// ✅ Use ReturnType
type IUserReturn = ReturnType<typeof getUser>;
const user: IUserReturn = { id: 1, name: 'Jhon' };
// Parameters
function updateUser(id: number, name: string) {}
// ✅ Use Parameters
type UpdateUserParams = Parameters<typeof updateUser>;
const args: UpdateUserParams = [1, 'Mery'];
#10 - Keyof & Index Types
Description: keyof gets all keys of a type. Useful for dynamic key access and constraints.
interface IUser {
id: number;
name: string;
email: string;
}
// ❌ Manual key type
type UserKeyManual = 'id' | 'name' | 'email';
// ✅ keyof
type UserKey = keyof IUser; // 'id' | 'name' | 'email'
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user: IUser = { id: 1, name: 'Jhon', email: 'jhon@example.com' };
const userName = getProp(user, 'name'); // "Jhon"
#11 - Note
Utility types enable reusable, concise, and type-safe code:
✅ Using these patterns improves REST API type safety, object manipulation, and reduces manual boilerplate.