JSON to TypeScript
Generate TypeScript interfaces or type aliases from a JSON sample. Nested objects become named interfaces, keys missing from some records become optional, and mixed values become unions.
How to Use
- Paste JSON into the input box, or drop a
.jsonfile. An array of several records from an API gives the most accurate types. - Set the Root name. For an array the element interface takes the singular, so
UsersgivesUserandtype Users = User[]. - Choose Interfaces or Type aliases, and tick export if the types live in a module.
- Read the cards and Show Work: every field says why it became optional, a union,
nullorany[], so you know which ones to tighten by hand. - Press Copy result or Download .ts.
Worked Example
Take two user records from an API, Ada and Alan (the default sample above), with the root name Users.
The top level is an array, so Users becomes a type alias and the element interface is named in the singular, User. Both objects are merged: their keys are id, name, active, roles, profile and lastLogin, 6 in all. Five of them appear in 2 of 2 objects and are required. lastLogin appears only in Alan’s record, 1 of 2, so it becomes lastLogin?: string.
Both roles arrays hold only strings, so the type is string[]. The two profile objects are merged into their own interface, Profile: city is in both, joined only in Ada’s, so it is optional too. Result: 2 interfaces, 8 fields, 2 optional, nested 3 levels deep (array → user → profile).
interface User {
id: number;
name: string;
active: boolean;
roles: string[];
profile: Profile;
lastLogin?: string;
}
interface Profile {
city: string;
joined?: string;
}
type Users = User[];
The common mistake: pasting only one record. From Ada alone the generator never sees lastLogin, so the field is missing from the type, and joined looks required — then assigning Alan’s record, which has no joined, fails to compile. Paste several real records so the differences between them show up.
Show Work
Inference Rules
From Untyped JSON to Structural Types
JSON was described by Douglas Crockford in the early 2000s and standardised as ECMA-404 in 2013 and RFC 8259 in 2017. It carries values but no declared types: the same key can hold a number in one record and a string in the next, and nothing says which keys must be present.
Microsoft released TypeScript in October 2012, designed by Anders Hejlsberg, with version 1.0 following in 2014. Its type system is structural — an object fits an interface if it has the right properties, whatever it is called — which is exactly what a JSON payload needs. Optional properties (?) were there from the start; TypeScript 2.0 (2016) added strict null checks, which is why a nullable field has to be written string | null, and TypeScript 3.0 (2018) added unknown as the safe alternative to any.
About This Tool
This tool reads a JSON sample and writes TypeScript interfaces or type aliases for it. Unlike a converter that looks at the first record only, it merges every object found in the same place, so keys that only some records carry are marked optional, values of different types become unions, and nested objects are merged into their own named interfaces.
Show Work gives the reason for each field’s type, and the cards highlight the ones that need a human decision: a field seen only as null, or an empty array typed any[]. Invalid JSON is reported with the line, column and the usual cause, such as a trailing comma. Everything runs in your browser; the JSON you paste or drop is never uploaded.
Related tools: JSON Formatter, JSON ⇄ CSV Converter, and Schema / JSON-LD Generator.
Frequently Asked Questions
How does it decide which fields are optional?
It compares every object that sits in the same place. A key found in all of them is required; a key found in only some gets a question mark. In the Users preset only Alan has lastLogin, so it is in 1 of 2 objects and becomes lastLogin?: string. A single object cannot show this, so paste an array of several real records.
What happens when the same key holds different types?
The types are joined into a union. Ids of 1, “a7” and 3 give id: number | string; values of 42, “high” and null give value: number | string | null. Inside arrays the union is wrapped in brackets: [9.5, 8, null] gives (number | null)[].
Why do some fields come out as null or any[]?
JSON only shows the values in your sample. A field whose only value is null can only be typed null, and an empty array has no element to look at, so it becomes any[]. Fix these by hand, for example deletedAt: string | null and tags: string[]. JSON also cannot tell a date string from any other string, or an integer from a decimal.
How are the nested interfaces named?
After the key they sit under, in PascalCase: profile becomes Profile and billing_address becomes BillingAddress. Arrays use the singular, so users gives User and categories gives Category. A repeated name gets a number (Address2), and a key that is not a valid identifier is quoted: "first-name": string.
Should I use interfaces or type aliases?
For object shapes both work. Interfaces can be extended with extends and merged by declaring them twice; type aliases can also name unions and tuples. A top-level array always needs an alias, which is why an array input ends with type Users = User[]; in either style. Pick whatever your linter or team prefers.
How do I use the JSON to TypeScript?
Simply type or paste your value and read the result, which refreshes the instant you change something. There is nothing to submit and nothing to wait for.
Do I need to install or sign up for anything?
Not at all — it runs in the browser with nothing to install and no account. After it loads once, it even works without an internet connection.
Is my information private?
Yes. Everything happens in your browser. Nothing you type is sent to a server or saved anywhere.
Common Use Cases
Typing an API response
Paste 20 records from a /users endpoint and get one merged User interface, with the fields that only some records carry marked optional.
Config files
A package.json with scripts and dependencies gives 3 interfaces: PackageJson, Scripts and Dependencies.
Webhook payloads
Type an incoming event before writing the handler, so a renamed or missing field is a compile error rather than undefined at run time.
Moving JavaScript to TypeScript
Log a real object from the old code, paste it here and start the migration with types that match the data the code already handles.
Checking data consistency
An unexpected union such as id: number | string shows that one record in a 500-row export stored its id as text.
Last updated: