Type HTTP PATCH Payload Shapes from JSON

Use json to typescript to model a HTTP PATCH payload — distinguish 'omit field', 'set field to null', and 'update field' in one type.

TypeScript Patterns

Detailed Explanation

PATCH Has Three States Per Field

REST PATCH semantics define three operations per field: leave unchanged (omit), clear (set to null), or update (provide value). Partial<T> collapses the second two and loses information.

Example PATCH JSON

{
  "title": "New title",
  "scheduled_at": null
}

The intent is: change the title, clear the scheduled_at, and leave every other field alone.

A Patch Mapped Type

type Patch<T> = {
  [K in keyof T]?: T[K] extends null | undefined ? T[K] : T[K] | null;
};

interface Post {
  id: number;
  title: string;
  body: string;
  scheduled_at: string;
  published: boolean;
}

type PostPatch = Patch<Omit<Post, "id">>;
// {
//   title?: string | null;
//   body?: string | null;
//   scheduled_at?: string | null;
//   published?: boolean | null;
// }

Three-State Handling on the Server

function applyPatch(post: Post, patch: PostPatch): Post {
  const next = { ...post };
  for (const key of Object.keys(patch) as (keyof PostPatch)[]) {
    const value = patch[key];
    if (value === null) {
      // Clear field — set to null in DB if column is nullable, else default
      (next as any)[key] = null;
    } else if (value !== undefined) {
      // Update field
      (next as any)[key] = value;
    }
    // value === undefined: leave unchanged
  }
  return next;
}

Why Not Partial<T>?

type WrongPatch = Partial<Post>;
// title?: string;  -- no way to express "clear"

Partial<T> lets you omit a field, but does not let you express "explicitly clear it." For PATCH semantics that mean clearing, this matters: a missing field and a null field have different effects.

When to Use This Shape

  • REST PATCH endpoints where omission and null are distinct operations.
  • JSON Merge Patch (RFC 7396) compatibility.
  • Form submissions where users clear fields by emptying them.
  • Optimistic UI updates that need to round-trip the user's "clear" intent.

When Not to Apply It

  • For PUT endpoints (full replacement), use the base type or Required<T>.
  • For partial creation forms (where missing means "use default"), Partial<T> is enough.

A purpose-built Patch<T> keeps your update API expressive and prevents the common bug of treating omitted fields as clear-instructions.

Use Case

Implementing a PATCH endpoint for a blog post where users can clear the scheduled publish date by setting it to null, while leaving every other field untouched.

Try It — JSON to TypeScript

Open full tool