100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Utility Types in TypeScript

Learn TypeScript's built-in generic utility types — Partial, Required, Readonly, Pick, Omit, and Record — for transforming existing types.

GenericsIntermediate9 min readJul 8, 2026
Analogies

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

typescript
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 dictionary

3. 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

typescript
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

text
Updating user 1 with { name: 'New Name' }
{ id: 1, name: 'Ada', email: 'ada@example.com' }
{ id: 1, name: 'Ada' }
Ada

6. Key Takeaways

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#TypeScriptProgrammingStudyNotes#Programming#UtilityTypesInTypeScript#Utility#Types#Syntax#Explanation#StudyNotes#SkillVeris