Utility Types

TypeScript ships a set of built-in generic types that transform an existing type into a new one — making every property optional, picking out a few fields, or locking a type down as read-only — instead of you rewriting a variant of the same interface by hand each time.

Partial and Required

TS partial.ts
interface Settings {
  theme: string;
  fontSize: number;
}

function updateSettings(current: Settings, changes: Partial<Settings>): Settings {
  return { ...current, ...changes };
}

const original: Settings = { theme: "light", fontSize: 14 };
const updated = updateSettings(original, { theme: "dark" });

console.log(updated);
Output
{ theme: 'dark', fontSize: 14 }

Partial<Settings> is the same shape as Settings but with every property made optional — perfect for an "update some fields" function where the caller shouldn't have to repeat fields they aren't changing.

Pick and Omit

TS pick-omit.ts
interface User {
  id: number;
  name: string;
  password: string;
}

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

const safe: PublicUser = { id: 1, name: "Nadia" };
console.log(safe);
Output
{ id: 1, name: 'Nadia' }

Omit<User, "password"> takes every property of User except password — useful for describing what's safe to send to a client. Pick<User, "id" | "name"> does the reverse, keeping only the listed properties.

Readonly

TS readonly.ts
interface Point { x: number; y: number; }

const p: Readonly<Point> = { x: 1, y: 2 };
p.x = 5;
tsc output
readonly.ts:4:1 - error TS2540: Cannot assign to 'x'
because it is a read-only property.

Readonly<Point> marks every property readonly at once, instead of writing that keyword in front of each field individually.

Note: like every TypeScript feature, utility types are checked at compile time only. Partial<Settings> guarantees the type checker won't complain about a missing field, but it does nothing at runtime — if that partial object came from parsing JSON from a network request, it could genuinely be missing fields your code assumes exist, and TypeScript has no way to catch that. Utility types reshape what the compiler checks; they don't validate what's actually there when the program runs.