ToolActToolAct

JSON to TypeScript

Automatically convert JSON data to TypeScript interfaces or type definitions

JSON Input
Lines: 1 | Chars: 0
TypeScript Output
// TypeScript type definitions will be generated automatically
Types: 0 | Lines: 1

What 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

  1. Paste JSON data in the left input box, or click the sample button to load an example
  2. Configure options: set root type name, choose interface/type style, whether to add export, etc.
  3. The corresponding TypeScript type definitions are automatically generated on the right
  4. Click the 'Copy' button to copy the generated type definitions to clipboard
  5. 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

Creating TypeScript types from API examplesPaste a JSON response, choose an interface or type alias style, and generate a root type with nested declarations. The tool merges object shapes found inside arrays, so repeated records can become a practical client-side model instead of a pile of unrelated examples.
Matching local naming conventions quicklyOptions for exported declarations, optional properties, and I/T prefixes let you adapt generated code to an existing project style before copying it. Renaming the root type is useful when turning a raw endpoint sample into a domain-specific model such as UserProfileResponse or InvoiceLine.
Bootstrapping mocks and fixtures safelyWhen a fixture grows faster than its type definition, converting the latest JSON sample reveals missing fields, nullable values, empty arrays, and mixed primitive types. It is a fast way to keep tests and demo data aligned with the shape the UI actually consumes.
Generate separate types for union variantsWhen a field can be a string, a number, or a nested object depending on the case, split the JSON samples and run each through the generator to get clean variant types instead of a noisy union. Hand-stitch the variants with a discriminated union afterward for proper narrowing in switch statements. Be careful that the tool marks every observed field, but never an unobserved one, so an empty array `[]` produces `unknown[]` rather than a phantom element shape, and a union inferred from mixed primitives often hides the discriminator you actually need to switch on.
Compare generated types against OpenAPI definitionsRun a real response through the converter, then diff the result against the official OpenAPI or backend-generated types. Catches missing fields, wrong nullability, and array item mismatches that a single sample cannot expose, especially when the API contract has drifted from production behavior. The generator's `?` flag tracks presence across multiple objects in the same input; a single sample never sets it, so a field that is genuinely required will still look optional until you feed a second payload that also omits it. Snake_case keys such as `user_id` are preserved verbatim rather than converted to camelCase, so the result lines up with server SDKs that emit snake_case JSON without renaming.

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.