Generate Readonly TypeScript Interfaces from JSON

Learn how to generate TypeScript interfaces with readonly properties from JSON data. Prevent accidental mutations and enforce immutability at the type level.

Advanced Patterns

Detailed Explanation

Immutable Types from JSON

By default, all properties on a TypeScript interface are mutable. Adding readonly prevents reassignment after construction, catching accidental mutations at compile time.

Example JSON

{
  "id": "usr_001",
  "createdAt": "2024-01-15T08:00:00Z",
  "name": "Alice",
  "permissions": ["read", "write"]
}

Generated TypeScript (readonly)

interface User {
  readonly id: string;
  readonly createdAt: string;
  readonly name: string;
  readonly permissions: readonly string[];
}

When to Use Readonly

  • IDs and timestamps — These are set by the server and should never be changed by client code.
  • Configuration objects — Parsed once, used everywhere, never mutated.
  • Redux/state management — Immutable state patterns benefit from readonly types.

Readonly vs Readonly<T>

You can apply readonly per-property or use the built-in utility type:

// Per-property
interface User {
  readonly id: string;
  name: string; // mutable
}

// All properties
type ImmutableUser = Readonly<User>;

Readonly<T> is shallow — nested objects remain mutable unless you apply it recursively:

type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};

Readonly Arrays

Use readonly string[] or ReadonlyArray<string> to prevent .push(), .pop(), and direct index assignment. This is especially important for arrays in state management where mutations cause bugs.

Practical Guideline

Make everything readonly by default and only remove it when you explicitly need mutation. This aligns with the principle of least privilege and catches a large class of bugs.

Use Case

You are building a Redux store and want all state slices to be immutable by default, so that accidental direct mutations are caught during development instead of causing subtle bugs at runtime.

Try It — JSON to TypeScript

Open full tool