Apply Partial, Required, and Readonly to JSON-Generated Types

Use json to typescript with mapped types — derive Partial, Required, and Readonly variants from a single base interface for create/update/store layers.

TypeScript Patterns

Detailed Explanation

One JSON Sample, Many Variants

A typical CRUD endpoint needs three closely related shapes: the row stored in the database, the input for creation (no id, no timestamps), and the input for partial updates. Mapped utility types derive all three from one source.

Base JSON

{
  "id": 7,
  "title": "First post",
  "body": "Hello world",
  "tags": ["intro", "meta"],
  "published": true,
  "created_at": "2024-03-15T10:30:00Z",
  "updated_at": "2024-03-15T10:30:00Z"
}

Generated Base Type

interface Post {
  id: number;
  title: string;
  body: string;
  tags: string[];
  published: boolean;
  created_at: string;
  updated_at: string;
}

Three Variants from One Source

// Stored row — readonly so domain code never mutates it
type StoredPost = Readonly<Post>;

// Create payload — server fills id and timestamps
type CreatePost = Omit<Post, "id" | "created_at" | "updated_at">;

// Update payload — every field optional except id
type UpdatePost = { id: number } & Partial<Omit<Post, "id" | "created_at" | "updated_at">>;

Why Derive Instead of Duplicate

If you wrote three independent interfaces, adding a category field to Post would silently leave CreatePost and UpdatePost out of date. With derivation, the new field flows automatically — and if you wanted to exclude it from updates, you would adjust the Omit clause in one place.

When Partial Goes Wrong

Partial<T> makes every field optional and undefined-able. If your update endpoint distinguishes "unset" (null) from "leave alone" (omitted), use a custom mapped type:

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

This makes every property either omitted, the original type, or explicit null — matching JSON Patch and HTTP PATCH semantics.

Generation Tip

Have your JSON-to-TS converter emit only the base Post interface, then add the derived types by hand. Mapped utilities are short, easy to review, and impossible for a generator to infer without explicit hints about which fields are server-managed.

Use Case

Modeling a blog post with a single Post interface and deriving CreatePost, UpdatePost, and StoredPost variants so adding a new field never requires touching three places.

Try It — JSON to TypeScript

Open full tool