The Shape Shapers: Partial, Pick, Omit

Advanced · 20 min read · ▶ live playground · ✦ checkpoint

Utility types are built-in generic helpers that reshape a type you already trust. Partial<T> makes properties optional, Required<T> makes them required, Readonly<T> makes them read-only, Pick<T, K> keeps selected keys, and Omit<T, K> drops selected keys.

Use them when a product shape already exists, but one workflow needs a draft, a patch, a public view, or a builder input. Section 11 showed the mapped-type machinery underneath; this lesson is about the daily decision: which built-in utility communicates the shape you mean?

Whole-Shape Shapers

Partial<T>, Required<T>, and Readonly<T> apply one modifier decision to every property in an object type.

  • Partial<T> means "same keys, but each key may be omitted."
  • Required<T> means "same keys, but every key must be present."
  • Readonly<T> means "same keys and values, but this view cannot write to them."

That makes them good for draft objects, normalized results, and read-only API surfaces:

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

ConfigDraft is useful while a builder is collecting fields, because an unfinished object can be honest about being unfinished. CompleteConfig belongs after finishConfig fills defaults, because callers can now rely on every property being present. LockedConfig is a compile-time read-only view; it does not freeze the runtime object, but it stops accidental writes through that name.

The rule is: use these whole-shape utilities only when the same decision really applies to every field. If only some fields should be optional or read-only, select those fields first.

Pick And Omit

Pick<T, K> keeps only the keys in K. Omit<T, K> keeps everything except the keys in K.

These are for boundary shapes: public views, creation inputs, and places where one type should derive from another instead of repeating a field list by hand.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

PublicAccount says exactly what can leave the server. NewAccountInput says exactly what a caller may provide before the system fills in ids, roles, hashes, and timestamps. The source shape still lives in one place: if AccountRecord.name changes type, the picked and omitted shapes follow.

One practical naming habit helps readers: name the workflow, not the utility. NewAccountInput is clearer than AccountWithoutIdAndPasswordHashAndRoleAndCreatedAt.

Here is the trap made visible with a type-level assertion:

type User = {
  id: string;
  email: string;
  name: string;
};
 
type WithoutTypo = Omit<User, "emial">;
 
type Expect<T extends true> = T;
type EmailWasRemoved = "email" extends keyof WithoutTypo ? false : true;
type Check = Expect<EmailWasRemoved>;

→ playground.ts:11:21 - error TS2344: Type 'false' does not satisfy the constraint 'true'.

The failing assertion expected email to be gone. It is still there. Pick<User, "emial"> would complain because Pick requires selected keys to be keyof User; Omit is looser, so key typos can silently remove nothing. When a hand-written omission list is risky, wrap it with a stricter local helper:

type StrictOmit<T, K extends keyof T> = Omit<T, K>;

You do not need that wrapper everywhere. You need to know the trap exists, especially in security-shaped code that omits passwords, tokens, or internal flags.

Patch Types And Builder Inputs

The most common beginner mistake is reaching for Partial<T> when only part of T is editable. A patch type should describe the fields the update endpoint actually accepts.

Combine the utilities:

  • Partial<Pick<T, Keys>> for editable update payloads.
  • Pick<T, Keys> for required builder fields.
  • Partial<Pick<T, KeysWithDefaults>> for builder fields that may be omitted because your function supplies defaults.
  • Omit<T, ServerFields> when the caller should provide everything except fields created by the system.
typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

Partial<Ticket> would allow id, createdAt, and updatedAt in the patch payload. Making them optional does not make them safe to accept. Partial<Pick<Ticket, ...>> says the update may provide a subset of the editable fields and nothing else.

Builder inputs are the same idea from the other direction. TicketBuilderInput requires the fields a caller must supply, and makes only the defaultable field optional. The created Ticket is a complete domain object, but the input is not pretending to be one.

These utilities reshape object properties. Next lesson moves from object shapes to set-shaped utilities: Record for dictionaries, and Exclude, Extract, and NonNullable for key and value unions.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion