What are utility types in TypeScript, such as Partial, Required, Pick, Omit and Record?
Utility types are built-in generic types that create a new type from an existing one. Partial makes all properties optional, Required makes them all required, Pick keeps only chosen keys, Omit removes chosen keys, and Record builds an object type from a set of keys and a value type.
Explain it simply
Think of a master student record in your college office with every field: name, roll number, phone, address, marks. Different forms need different slices of it:
- The "update your details" form lets you change any field, but you do not have to fill all of them. That is
Partial. - The hall ticket only shows name and roll number. That is
Pick. - The public result list shows everything except phone and address. That is
Omit.
Utility types let you derive these shapes from the one master type, instead of rewriting them by hand. When the master type changes, every derived type updates automatically.
Example
interface Student {
id: number;
name: string;
email: string;
phone?: string;
}
// Partial<T>: every property optional
type StudentUpdate = Partial<Student>;
function updateStudent(id: number, changes: StudentUpdate): void {}
updateStudent(1, { email: "new@example.com" }); // OK: only one field
// Required<T>: every property required (removes ?)
type CompleteStudent = Required<Student>; // phone is now required
// Pick<T, Keys>: keep only these properties
type HallTicket = Pick<Student, "id" | "name">;
// { id: number; name: string }
// Omit<T, Keys>: remove these properties
type NewStudent = Omit<Student, "id">; // id is created by the database
// Record<Keys, Value>: object with these keys and this value type
type Grade = "A" | "B" | "C";
const gradeCount: Record<Grade, number> = { A: 12, B: 20, C: 5 };
// Missing a key or adding "D" is an error
// Readonly<T>: no reassignment of properties
const config: Readonly<Student> = { id: 1, name: "Asha", email: "a@x.com" };
// config.name = "Ravi"; // Error: name is read-onlyHow it works
Most utility types are mapped types: they loop over the keys of a type and transform each property. You could write Partial yourself:
type MyPartial<T> = { [K in keyof T]?: T[K] };
type MyPick<T, K extends keyof T> = { [P in K]: T[P] };
type MyRecord<K extends keyof any, V> = { [P in K]: V };Omit is built from Pick and Exclude: it picks every key except the ones you exclude.
Other useful ones:
Readonly<T>: all properties read-only.ReturnType<typeof fn>andParameters<typeof fn>: types taken from a function.Awaited<T>: the value type inside aPromise.NonNullable<T>: removesnullandundefined.Exclude<T, U>andExtract<T, U>: filter members of a union.
Real-world use
Partial<T>for PATCH requests and form state where fields are filled one by one.Omit<T, "id" | "createdAt">for "create" payloads where the server generates those fields.Pick<T, ...>to type a small card component that needs only a few fields of a big object.Record<string, number>for lookup maps, andRecord<Status, string>to make sure every status has a label.ReturnTypeto type the result of a function you do not control.
Common mistakes
- Expecting
PartialandReadonlyto be deep. They only affect top-level properties; nested objects are unchanged. - Thinking
Readonlyfreezes the object at runtime. It is a compile-time check only; useObject.freezefor runtime. - Typo in
Omitkeys.Omitaccepts any string, soOmit<Student, "emial">compiles silently and removes nothing.Pickdoes check that keys exist. - Using
Record<string, T>and assuming every key lookup returns aT. Without thenoUncheckedIndexedAccessoption,obj["missing"]is typed asTeven though it isundefinedat runtime.
What to say in the interview
"Utility types are built-in generics that transform an existing type, so I keep one source of truth and derive the rest. Partial makes all properties optional, which I use for update payloads; Required does the opposite; Pick keeps selected keys and Omit removes them, for example omitting id for a create request; and Record builds an object type from a key type and a value type, useful for lookup tables. They are implemented as mapped types over keyof T. I keep in mind that they are shallow and only exist at compile time."
Follow-up questions
- How would you write Partial yourself?
- How would you make a DeepPartial type?
- What is the difference between Omit and Exclude?
- How do ReturnType and Parameters work?
Go deeper
Read these once the basics above feel easy.
A recursive DeepPartial can be written as type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] };, though real versions need special cases for arrays, functions and dates. Omit is not checked because its definition is Omit<T, K extends keyof any>; if you want typo protection, define your own type StrictOmit<T, K extends keyof T> = Omit<T, K>;. ReturnType is built with a conditional type and infer: T extends (...args: any) => infer R ? R : any.