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
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);
{ 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
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);
{ 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
interface Point { x: number; y: number; }
const p: Readonly<Point> = { x: 1, y: 2 };
p.x = 5;
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.
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.