JSON to TypeScript
Automatically convert JSON data to TypeScript interfaces or type definitions
// TypeScript type definitions will be generated automaticallyWhat is JSON to TypeScript?
JSON to TypeScript analyzes a JSON structure and generates TypeScript interfaces or type aliases from it. Teams often use it when API responses, configuration files, sample payloads, or mock data need to be brought into a typed frontend or Node.js project quickly. The tool can infer objects, arrays, nested properties, optional fields, and basic value types, turning raw data into a practical starting point for safer code completion and compile-time checks. It still needs human review: one JSON example may not include every possible field, empty arrays do not reveal item shape, and real APIs may return null or variant objects. Treat the output as a strong draft, not an unquestioned schema.
How to Use
How to use
- Paste JSON data in the left input box, or click the sample button to load an example
- Configure options: set root type name, choose interface/type style, whether to add export, etc.
- The corresponding TypeScript type definitions are automatically generated on the right
- Click the 'Copy' button to copy the generated type definitions to clipboard
- Paste the type definitions into your TypeScript project to use
Type Generation Tips
- Use representative JSON samples. A single example cannot reveal every optional field, nullable value, or union type in a real API.
- Review generated names before committing them, especially for nested objects where automatic interface names may be too generic.
Use Cases
Technical Principle
JSON to TypeScript is fundamentally a 'structure inference' process. The tool walks each node of the JSON tree recursively, mapping the runtime type of each value to a TypeScript type: string -> string, number -> number, boolean -> boolean, null -> null, object -> sub-interface, array -> array of element type. The output is a set of TypeScript `interface` declarations (or `type` aliases if the 'type' output mode is selected) plus any nested interfaces extracted from object-valued fields. The hard part of the inference is the union-vs-single-type decision for arrays. When every element of an array has the same type, the output is `T[]` (homogeneous array). When elements have different types, the output is the union of those types: `[1, 'a', true]` becomes `(number | string | boolean)[]`. When the input is an object that mixes literal and structured values (e.g. `{ id: 1, name: 'Alice', tags: ['admin', 'editor'] }`), each field gets its own type; for objects with mixed primitive values the page emits a single interface, not a union, because that's what TypeScript code expects (and matches JSON object's nature as a record type). Multiple sample objects (the typical use case: paste a JSON array) are merged by intersecting their shapes. Fields present in every sample become required; fields missing in any sample become optional (`fieldName?: T`). This is the right behaviour for API responses: a 200 OK response is one shape, a 404 response is another, and the union of both is the right TS type. For a single sample, every field is required. Name normalisation is where most 'JSON to TS' tools fall short. JSON keys are usually snake_case (`user_id`, `created_at`) or camelCase (`userId`, `createdAt`); TypeScript convention is PascalCase for interface names (`UserProfile`, `AuditEntry`) and camelCase for property names. The page converts JSON keys to camelCase (or preserves them if they are already camelCase or single-word) and PascalCase for interface names. Inner objects are extracted into named interfaces: `{ user: { name, age } }` becomes `interface User { name: string; age: number; }` and the parent becomes `interface Root { user: User; }`. Recursive structures (a tree node with a `children: TreeNode[]` field) get a self-reference using a forward `type` declaration instead of `interface` so the cycle is well-defined. The 'root' toggle is the difference between an array root and an object root. A JSON array becomes `type Root = Item[]` or `interface Root { items: Item[]; ... }` depending on the wrap-array toggle. A JSON object becomes a flat interface at the top level. Date strings (RFC 3339 / ISO 8601) can be tagged as `Date` instead of `string`; UUIDs, emails, and URLs can be tagged as branded types (`type UUID = string & { readonly __brand: 'UUID' };`) when the strict-mode toggle is on. The JSDoc generation is heuristic: a field named `email` (and whose value matches a basic email regex) gets a comment like `/** user email */` above the declaration. The same goes for `id`, `name`, `url`, `created_at`/`createdAt`, `updated_at`/`updatedAt`, `description`, `title`. The comments are best-effort and do not pretend to be human-written; they're a starting point that humans will edit. The page also emits `export` modifiers so the output can drop into any TS file as-is, and a `// generated by json-to-typescript, do not edit by hand` header to discourage manual edits that would get clobbered the next time the schema regenerates.
- Atomic type mapping: JSON's string/number/boolean/null map one-to-one to TypeScript's string/number/boolean/null. Large integers (>2^53) need BigInt; the page tags them as `bigint` when the value exceeds Number.MAX_SAFE_INTEGER.
- Array type inference: walk all elements and take the union of their types; identical types collapse to `T[]`, distinct types form `(A | B | C)[]`. Empty arrays become `unknown[]` since the element type is unknown.
- Nested object extraction: inner objects are automatically lifted into named interfaces. Recursive structures use `type Name = ...` (not `interface`) so the forward reference is well-defined in TypeScript's type checker.
- Optional field detection: when multiple sample objects are merged, fields present in every sample become required; fields missing in any sample become optional (`fieldName?: T`). Single-sample input marks every field required.
- Naming convention: JSON keys are converted to camelCase for property names (user_id -> userId, created_at -> createdAt) and PascalCase for interface names (UserProfile, AuditEntry). Single-word keys are preserved; numbers in keys are preserved; leading digits are prefixed with underscore to stay valid TS identifiers.
- Root toggle: array root -> `export type Root = Item[]` (or wrapped in `{ items: Item[]; }` if wrap-array is on); object root -> a single `export interface Root { ... }`. Recursive structures get a stub forward declaration first.
- JSDoc comments: heuristic comment generation for known field names (email, id, name, url, createdAt, updatedAt, description, title). Output includes `export` modifiers and a `// generated, do not edit by hand` header to discourage manual edits.
- Strict mode: opt-in to branded types (UUID, Email, URL, Date), readonly modifiers, exactOptionalPropertyTypes, and noUncheckedIndexedAccess. The page emits `type UUID = string & { readonly __brand: 'UUID' };` when strict mode is on, so downstream code can distinguish a raw string from a typed value.
Examples
Simple Object to interface
{"id": 1, "name": "Alice", "active": true}
->
interface User {
id: number;
name: string;
active: boolean;
}Nested Object Generates Sub-Interfaces
{"user": {"name": "Alice", "address": {"city": "NYC"}}}
->
interface Root {
user: User;
}
interface User {
name: string;
address: Address;
}
interface Address {
city: string;
}Array to readonly Tuple
[{"id": 1}, {"id": 2}]
->
interface Item {
id: number;
}
type Root = readonly Item[];FAQ
Which TypeScript constructs does it generate?
Interfaces by default - one per object shape. Optional fields use the ? marker. Arrays become T[]. Mixed-type arrays become a union. Strings of recognised formats (e.g. ISO dates) stay as string unless you opt in to branded types. Some pages offer 'type' aliases instead of 'interface'.
How are nested objects handled?
Each distinct nested shape gets its own interface, named after the property path (User, UserAddress, UserPreferences). If two properties share the same shape, the generator may or may not deduplicate depending on settings - check the output and consolidate manually if needed.
Are properties required or optional?
By default, properties present in the sample are marked required. Properties that appear in some array elements but not others are marked optional with `?`. Toggle 'all optional' if your sample is one example among many possible shapes.
How does it handle null and undefined?
null in JSON becomes `null` in TypeScript (not undefined). A property that is sometimes a string and sometimes null becomes `string | null`. JSON has no undefined, so the generator never emits `undefined` directly.
What about union types and discriminated unions?
When an array contains mixed object shapes, the generator emits a union of interfaces. It does not auto-detect a discriminator field; manually rewrite into a discriminated union (e.g. 'kind' tag) if you want exhaustive type narrowing.
Is the JSON uploaded?
No. Conversion runs in your browser via a JS-based JSON parser and string template. Pasted JSON does not leave your device.
Will it match what my backend actually returns?
Only as well as the sample. Always validate runtime data against a schema (Zod, io-ts, Yup) - TypeScript types are erased at runtime and won't catch a backend that drifts from the example.