Back to blog
Tutorials9 min read

JSON to TypeScript: Generate Types and Interfaces

Turn JSON samples into TypeScript interfaces. Learn how type inference works, interface vs type alias, optional and nullable fields, arrays and unions, and how to refine generated types for production.

By Saifur Rahman Shochib

Hand-writing TypeScript interfaces for a large API response is tedious and error-prone. Generating them from a real JSON sample is faster and more accurate — you get compile-time safety and editor autocomplete over data that was previously untyped. This guide explains how JSON-to-TypeScript generation works, the choices it makes about optional and nullable fields, and how to turn a good first draft into production-ready types.

Why generate types from JSON?

TypeScript only protects you if your data is typed. An API response typed as any silently defeats the compiler. Generating an interface from a representative JSON sample gives you autocomplete, refactoring safety, and red squiggles when you access a field that does not exist — all derived from the actual shape of the data rather than a guess.

How type inference works

A generator walks the JSON and maps each value to a TypeScript type: strings to string, numbers to number, booleans to boolean, and objects to their own named interface. Arrays become typed arrays, so a list of numbers becomes number[] and a list of objects becomes an interface array. Each nested object produces its own interface, yielding a complete, readable type graph instead of one giant inline shape.

JSON samplejson
{ "id": 1, "name": "Ada", "tags": ["x"], "active": true }
Generated interfacetypescript
interface User {
  id: number;
  name: string;
  tags: string[];
  active: boolean;
}

Interface versus type alias

TypeScript can express object shapes as either an interface or a type alias. Interfaces are conventional for object shapes, support declaration merging, and produce clean extends hierarchies — a good default for generated models. Type aliases shine for unions and primitives. For JSON-derived object models, interfaces are usually the clearer choice, but either works and the two are largely interchangeable for plain data.

Optional and nullable fields

This is where a single sample falls short. If a field is absent in your example, the generator cannot know it exists; if it is present but sometimes null in reality, the generated type will not reflect that. After generating, mark fields that may be missing as optional (name?: string) and fields that may be null as a union (name: string | null). Feeding the generator a sample that includes the nullable and optional cases produces better output up front.

Refine optional and nullable fields by handtypescript
interface User {
  id: number;
  nickname?: string;        // may be absent
  deletedAt: string | null; // may be null
}

Arrays, unions, and mixed types

Arrays of mixed types become a union element type, such as (string | number)[]. When the same field holds different types across records — a number here, a string there — you will want a union like string | number rather than the type from a single example. Empty arrays are ambiguous because there is nothing to infer from, so they often default to any[] or unknown[] and deserve a manual pass.

Refining generated types for production

  • Add optional markers (?) to fields that can be absent
  • Widen single-sample types to unions where a field varies (string | null, number | string)
  • Replace string with literal unions for known enums, such as 'active' | 'archived'
  • Rename auto-generated nested interface names to something meaningful
  • Treat the output as an accurate first draft you review, not a finished contract

From sample to schema

Types generated from one example describe that example. When you need a runtime contract that validates incoming data — not just compile-time types — generate a JSON Schema from the same sample and validate against it with a library like Zod or Ajv. Use the JSON to TypeScript tool to scaffold your interfaces and the JSON Schema Generator when you also need runtime validation, keeping a single source of truth for the shape.

Frequently asked questions

How do I convert JSON to a TypeScript interface?

Paste a representative JSON sample into a JSON-to-TypeScript generator. It infers a type for each value and emits named interfaces for nested objects, giving you a complete typed model you can drop into your code.

Should I use interface or type for JSON models?

Interfaces are the conventional, readable choice for object shapes derived from JSON and support extends and declaration merging. Type aliases are better for unions and primitives. For plain data models the two are largely interchangeable.

Why are some generated fields wrong or too narrow?

Types are inferred from a single sample, so a field that is null or absent in reality but present in your example will be mistyped. Review the output and add optional markers and union types where the data actually varies.

How do I handle fields that can be null?

Mark them as a union with null, for example name: string | null. A generator cannot infer nullability from a non-null sample, so this is a manual refinement — or include a null case in the sample you feed it.

Do generated types validate data at runtime?

No. TypeScript types are erased at compile time and do not check incoming data. For runtime validation, generate a JSON Schema and validate with a library like Zod or Ajv alongside your generated types.

Related tools

Try these tools

Keep reading

Related guides