1. Introduction
TypeScript ships with a set of built-in generic utility types that transform an existing type into a new one, rather than requiring you to write the transformed type from scratch. They're implemented internally using generics, mapped types, and conditional types, but you use them simply by supplying a type argument — e.g. Partial<User> — just like any other generic type.
Cricket analogy: Just as a coach doesn't rebuild a player's whole technique from scratch but applies a targeted throwdown drill to an existing batting stance, Partial<User> transforms an existing User type rather than redefining it.
2. Syntax
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>; // all properties optional
type RequiredUser = Required<User>; // all properties required
type ReadonlyUser = Readonly<User>; // all properties readonly
type UserPreview = Pick<User, "id" | "name">; // subset of keys
type UserWithoutEmail = Omit<User, "email">; // exclude keys
type UserMap = Record<number, User>; // key/value dictionary3. Explanation
Each utility type transforms an existing type rather than being written from scratch: Partial<T> makes every property of T optional, useful for update/patch functions where a caller supplies only the fields they want to change. Required<T> does the opposite, making every property mandatory, even ones that were originally marked optional with ?. Readonly<T> makes every property immutable after initial assignment, so reassigning a property is a compile-time error.
Cricket analogy: Partial<PlayerStats> lets a scorer submit only the boundary count for a patch update mid-over, Required<PlayerStats> forces a post-match report to include every field like strike rate, and Readonly<PlayerStats> locks a Test average once the series ends.
Pick<T, K> builds a new type containing only the listed keys K (a union of string literal keys) from T, ideal for API responses that expose a subset of an internal model. Omit<T, K> is the inverse — it keeps every property of T except the ones listed in K, commonly used to strip sensitive fields like passwords before sending data to a client. Record<K, V> builds a dictionary/map type where every key of type K maps to a value of type V, useful for lookup tables keyed by id, string, or enum.
Cricket analogy: Pick<Player, 'name'|'battingAvg'> builds a scoreboard-display type with only those fields, Omit<Player, 'medicalRecords'> strips sensitive data before publishing a profile, and Record<TeamName, Player[]> builds a squad-lookup dictionary keyed by team.
All of these are themselves generic types under the hood — for example Partial<T> is defined roughly as type Partial<T> = { [P in keyof T]?: T[P] }, using a mapped type over keyof T. Understanding generics is what makes it possible to read and eventually write your own utility types.
Cricket analogy: Just as knowing every fielding position lets a captain design a brand-new custom field setup rather than reuse a standard one, understanding generics and 'keyof' lets you write your own utility type instead of only using Partial.
Gotcha: Partial<T> and Readonly<T> are both shallow, not deep/recursive. Partial<{ address: { city: string } }> makes address itself optional, but if address is present, its nested city property is still required — nested objects are not recursively made partial. The same applies to Readonly: it does not freeze nested objects.
4. Example
interface User {
id: number;
name: string;
email: string;
password: string;
}
function updateUser(id: number, changes: Partial<User>): void {
console.log(`Updating user ${id} with`, changes);
}
updateUser(1, { name: "New Name" }); // only name needed, thanks to Partial
function toPublicUser(user: User): Omit<User, "password"> {
const { password, ...publicUser } = user;
return publicUser;
}
const fullUser: User = { id: 1, name: "Ada", email: "ada@example.com", password: "secret" };
console.log(toPublicUser(fullUser));
type UserPreview = Pick<User, "id" | "name">;
const preview: UserPreview = { id: 1, name: "Ada" };
console.log(preview);
type UsersById = Record<number, User>;
const usersById: UsersById = { 1: fullUser };
console.log(usersById[1].name);5. Output
Updating user 1 with { name: 'New Name' }
{ id: 1, name: 'Ada', email: 'ada@example.com' }
{ id: 1, name: 'Ada' }
Ada6. Key Takeaways
Practice what you learned
1. What does `Partial<User>` do to the User interface?
2. Given `interface User { address: { city: string } }`, does `Partial<User>` make `address.city` optional too?
3. What is the relationship between `Pick<T, K>` and `Omit<T, K>`?
4. Which utility type would you use to build a dictionary type where numeric ids map to User objects?
5. How is `Partial<T>` conceptually implemented internally by TypeScript?
Was this page helpful?
You May Also Like
Generics in TypeScript
Learn how TypeScript generics let you write reusable, type-safe code that works across many types without losing type information.
Mapped Types in TypeScript
Transform every property of an existing type into a new type using `{ [K in keyof T]: ... }`, with modifiers and key remapping.
keyof and typeof Operators in TypeScript
Use `keyof T` to get a union of a type's property names, and `typeof x` in a type position to extract the type of a value.
Interfaces in TypeScript
Learn how TypeScript interfaces describe the shape of objects using structural typing, and why they are the primary tool for contract-based design.
Type Aliases in TypeScript
Use `type` to give a name to any type -- primitives, unions, tuples, function signatures, and more.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics