3 Topics
Chapter•70 Minutes

Utility Types and Mapped Types

Use built-in utility types to transform object contracts and understand the mapped-type pattern behind them.

Partial, Required, Pick, and Omit

Built-in utility types transform existing types. Partial<T> makes properties optional; Required<T> makes them required; Pick<T, K> selects a subset of keys; Omit<T, K> removes selected keys. These are especially useful for create/update DTOs and separating public data from internal fields.

Utility types do not alter runtime objects. A Partial<User> is a compile-time description, not a function that removes or changes properties. Validate business rules separately: a type may permit an optional field while the domain still requires at least one field in a particular operation.

Create and update DTOs
type User = {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
};

type CreateUserInput = Pick<User, "name" | "email">;
type UpdateUserInput = Partial<CreateUserInput>;
type PublicUser = Omit<User, "createdAt">;
💡 Pick selects fields for creation, Partial allows updates to include only changed fields, and Omit removes createdAt from the public type.
Key Takeaways
  • Utility types transform types, not runtime data.
  • Use Pick/Omit to define intentional API shapes.
  • Partial is useful for patches but may need extra business validation.
Knowledge Check

What does `Partial<T>` do?

Readonly, Record, Extract, and Exclude

Readonly<T> makes properties readonly at the type level. Record<K, V> constructs a key/value object. Extract<T, U> keeps union members assignable to U, while Exclude<T, U> removes them. NonNullable<T> removes null and undefined; ReturnType<F> obtains a function's return type.

These tools become easier when you think in terms of unions and transformations. They do not inspect runtime values. For complex public types, consider naming intermediate types so errors and editor tooltips remain understandable.

Transform a union
type Status = "queued" | "running" | "done" | "failed";

type FinishedStatus = Extract<Status, "done" | "failed">;
type ActiveStatus = Exclude<Status, "done" | "failed">;

type MaybeUser = { id: string } | null | undefined;
type UserOnly = NonNullable<MaybeUser>;
💡 Extract selects the matching union members; Exclude removes matching members; NonNullable removes nullish members.
Key Takeaways
  • Extract filters a union by assignability.
  • Exclude removes union members assignable to a target.
  • NonNullable removes null and undefined from a type.
Knowledge Check

What is `Exclude<"a" | "b" | "c", "b">`?

Mapped Types and Key Remapping

Mapped types iterate over keys in a type and produce a new type. The basic pattern is { [K in keyof T]: ... }. You can add or remove modifiers and, in modern TypeScript, remap keys with as. Mapped types power many utility types and can model transformations such as making every field nullable or generating event names.

Use mapped types when the transformation is systematic. For a simple domain contract, an explicitly written type may be easier to maintain. Complex recursive or conditional mapped types can create slow type checking and difficult error messages.

Make every property a getter signature
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

type User = { name: string; age: number };
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }
💡 The mapped type iterates over the keys of T, remaps each key using a template literal type, and returns a function producing the original property type.
Key Takeaways
  • Mapped types transform keys systematically.
  • Key remapping uses as in a mapped type.
  • Keep advanced type transformations readable and purposeful.
Knowledge Check

Which syntax iterates over the keys of T in a mapped type?