Free JSON to TypeScript
Paste JSON and get TypeScript interfaces instantly.
How to Use
- Paste your JSON in the left pane (object or array).
- Click Convert · TypeScript interfaces appear on the right.
- Adjust the root name and toggle options as needed.
- Click Copy Output to copy the generated code.
Frequently Asked Questions
Does it handle nested objects?
Yes. Each nested object gets its own named interface. Array items are also analyzed to determine the element type.
What happens with mixed-type arrays?
If an array contains items of different types, the converter uses a union type (e.g., string | number).
Does it support JSON arrays at the root?
Yes. If the root value is an array, the converter analyzes its items and produces the appropriate interface plus a type alias for the array.
What the Converter Actually Does
It parses your JSON, walks every value, and emits a set of TypeScript interface declarations describing the shape. Nested objects each get their own named interface, derived from the parent property key in PascalCase (a user field becomes a User interface). When two nested objects collide on name, the second gets a numeric suffix (User, User2). At the root, an array becomes a type Root = RootItem[]; alias plus the RootItem interface; an object becomes a single Root interface (or whatever name you put in the "root name" field).
Sample input and output:
// Input
{
"id": 42,
"name": "Ada",
"active": true,
"tags": ["admin", "billing"],
"profile": { "bio": "Programmer", "link": null }
}
// Output
export interface Root {
id: number;
name: string;
active: boolean;
tags: string[];
profile: Profile;
}
export interface Profile {
bio: string;
link: unknown;
}
The JSON → TypeScript Type Mapping
| JSON value | Generated TS |
|---|---|
"hello" | string |
42 or 3.14 | number (TS has no separate integer type) |
true / false | boolean |
null | unknown (the converter can't tell whether the field is nullable or just absent in this sample) |
[] | unknown[] |
[1, 2, 3] | number[] |
[1, "x"] | (number | string)[] |
{ "a": 1 } | named interface |
For root-level arrays of objects, the converter merges keys across all items into a single representative interface. That's a fair approximation when the items share a shape, but it loses information when shapes genuinely diverge (e.g. a heterogeneous events array). For that case, you'd need to write a discriminated union by hand after generation.
Why interface for Objects, type for the Root Array
The official TypeScript Handbook gives a one-line heuristic: "If you would like a heuristic, use interface until you need to use features from type." Both are structurally typed, both can describe object shapes, but interface wins for object shapes because it supports declaration merging (re-opening the interface to add fields), works cleanly with extends, and shows up by name in compiler errors. type aliases are required when you need unions of primitives, intersections of multiple types, or to name a non-object thing, which is why the root-level array gets a type Root = RootItem[]; alias rather than an interface.
A note on naming: the converter uses PascalCase without the legacy I prefix (so User rather than IUser). Modern TS style guides (Google's, Microsoft's, the React community's) almost universally drop the prefix. Property names match the JSON keys exactly (camelCase if the JSON uses camelCase, snake_case if it uses snake_case). Keys that aren't valid JS identifiers (containing hyphens, spaces, or starting with a digit) are quoted: "foo-bar": string;.
What This Tool Doesn't Do (Be Honest About Limits)
A single JSON example carries less information than a real schema, so several things that would be nice to detect aren't possible from the input alone:
- Optional fields. Every property in your example becomes required. If the real API sometimes omits a field, you need to add the
?by hand:email?: string;. Or run the converter on multiple examples and union the results. - Nullable detection. A
nullin the example becomesunknown; the converter can't know whether the field is genuinely nullable (string | null) or just happened to be null in this sample. Adjust by hand based on the API contract. - String literal unions. A status field with the value
"active"becomesstring, not"active" | "inactive" | "pending". The converter can't see the other values. - Date type detection. An ISO 8601 string like
"2024-04-12T10:00:00Z"stays asstring. JavaScript'sDateisn't a JSON type. - Discriminated unions. If a root array contains items of different shapes (events of different types, polymorphic records), the converter merges all keys into one interface rather than producing a tagged union. For real heterogeneous data, you'll want to write the union by hand.
- Number precision. JavaScript's
numbersafely represents integers up to 253−1. Larger integer IDs lose precision when parsed byJSON.parse; if your API returns 64-bit integers, the safest pattern is to send them as strings and type them asstring.
When to Use This Tool
- Bootstrapping types from a real API response. Hit the endpoint, paste the JSON, get a starting set of interfaces. Tweak by hand for optional fields and string literals.
- Adding types to an untyped third-party SDK. Many older JS libraries ship without types. Generating from a sample response is faster than reading the docs.
- Sharing types between front-end and back-end. Generate from one canonical example and copy into both codebases (or share via a published types package).
- Migrating a JS codebase to TypeScript. Generate types from real data flowing through the app and gradually annotate function signatures.
- Generating types for a config file. Strict typing makes config errors visible at compile time.
- Documenting the shape of an internal data file. The generated interface doubles as machine-readable docs.
Code-First vs Schema-First vs Direct (Where This Tool Sits)
Three common workflows for getting TypeScript types from runtime data:
- Code-first runtime validators: Zod, Yup, Effect Schema, Joi. You write a validator schema in JS, it gives you both runtime parsing and a TypeScript type via inference. The schema is the source of truth. Best when validation matters as much as the type.
- Schema-first: write a JSON Schema or OpenAPI spec, generate TypeScript types via tools like
quicktype,json-schema-to-typescript, oropenapi-typescript. Best when an API contract spans multiple languages. - Direct sample inference (this tool, plus quicktype's plain-JSON mode), paste real data, get types. Fastest path; weakest validation guarantees because nothing checks runtime data against the type.
The right choice depends on whether your concern is mostly static type-checking (this tool is great) or also runtime safety (reach for Zod / Effect Schema instead).
Common Mistakes
- Treating generated types as a finished spec. They're a 70%-done starting point. Add
?for optional fields,| nullwhere genuinely nullable, string literal unions where applicable. - Generating from a single example when the API has variants. A user object that sometimes has an
emailand sometimes doesn't will generate as "email is required". Run multiple examples and union the results, or hand-edit. - Marking everything
readonlyautomatically. Useful for immutable data flows but wrong for state objects you mutate. Use thereadonlytoggle thoughtfully. - Trusting precision on large integer IDs. JavaScript's
numbertops out at 253−1. For 64-bit IDs, transmit as strings and type asstring. - Confusing
interfacewithtype. Different tools, different trade-offs. The TypeScript Handbook's heuristic is "use interface unless you need a feature only type provides," exactly what this tool does. - Pasting confidential JSON into a server-side converter. Real API responses often contain customer data, credentials, internal IDs. Browser-only conversion (this tool) keeps the data on your machine.
More Frequently Asked Questions
Why did all my fields get marked as required?
Because a single JSON example can't tell the converter which fields are sometimes absent in the real API. Every key that appears in the example becomes required. If you know a field is optional, add a trailing ? by hand: email?: string;. For multi-sample optionality detection, the more sophisticated quicktype CLI handles it natively.
Why is my null field typed as unknown?
Because the converter can't tell from a single null whether the field is always nullable (string | null) or whether it just happened to be null in this sample (string). unknown is the conservative default; TypeScript will force you to narrow the type before using the value, which is safer than any. Edit to string | null by hand once you know the API's intent.
Should I use interface or type?
The official TypeScript Handbook's heuristic: use interface until you need a feature only type provides, primarily union types, intersection types, or naming a primitive. This converter follows that rule: interface for every object shape, type for the root array alias. Some style guides recommend defaulting to type instead (Matt Pocock has argued the case publicly) but the conventional default is interface for objects.
Will my JSON be uploaded anywhere?
No. The conversion is a self-contained JavaScript IIFE that parses your JSON via JSON.parse, walks the value, and writes the TypeScript output to a textarea. There's no fetch, no analytics call carrying the JSON content, no server. This matters because real API responses typically contain customer data, internal IDs, or credentials that you don't want flowing through a third party.
What about JSDoc, Zod, or runtime validation?
This tool emits compile-time TypeScript types only: no runtime validators (no Zod / Yup / Effect Schema), no JSDoc comments. If you need runtime validation that doubles as a static type, write the schema in Zod and use Zod's z.infer<typeof Schema> to derive the type. If you have a JSON Schema and want both runtime validation and TS types, json-schema-to-typescript + AJV is the conventional pairing.
Why is there no I prefix on the interface names?
Because most modern TypeScript style guides drop it. Google's TypeScript Style Guide recommends against it explicitly; the React community has converged on PascalCase without prefix. The legacy IUser convention came from C# / Java influence on early TypeScript; current practice is plain User.