Skip to main content
CuteJSON
↑↓ Navigate Enter Select Esc Close

How to convert JSON to TypeScript interfaces

Published 2 min read By Akash Kinkar Pandey

Writing TypeScript types by hand for a JSON response you already have is repetitive and easy to get subtly wrong on the first pass, especially once a field is optional or a value is nested three levels deep.

Using the JSON to TypeScript generator

Paste a document, or several records in an array, name the root type, choose interfaces or type aliases, and press Generate types. The type inference is shared with the schema generator on this site, so the two pages agree about what your document actually contains and differ only in how they write the answer down.

Given

[
  {"user": {"name": "a"}, "tags": [], "meta": {}},
  {"user": {"name": "b"}, "tags": ["x"], "meta": {}, "extra": true}
]

the output is:

export interface RootItem {
  user: User
  tags: string[]
  meta: Record<string, unknown>
  extra?: boolean
}

export interface User {
  name: string
}

export type Root = RootItem[]

Why nested objects get their own named type

An inline type three levels deep is unreadable, and nothing else in the file can refer to it. Names come from the key that holds the object, singularised inside an array (RootItem from Root), and a repeated name gets a number appended so the output always compiles without a collision.

What the optional mark actually promises

extra?: boolean means the key was missing from at least one record in your sample, exactly the same rule the schema generator uses for required. It does not mean the field is guaranteed optional across your whole API, only that your sample cannot prove it is always present. Read the mark as evidence from your data, not as a guarantee from the source system.

Empty values

An empty object becomes Record<string, unknown> rather than an empty interface, since an empty interface accepts anything and says nothing useful about the shape. An empty array becomes unknown[], because there is nothing inside it to infer a element type from, and inventing one would be a guess dressed up as a fact.

Why every number is just number

TypeScript has no separate integer type, so a field that is always a whole number in your sample is still typed number, the same as one that contains decimals. That mirrors how TypeScript itself models numbers, so the generated types compile and behave exactly like hand-written ones.

Back to all posts