JSON to TypeScript
// Output appears here
Most TypeScript codebases that talk to an external API have, somewhere in their lifetime, a moment when a developer pastes a sample JSON response into a chat with the team and asks 'can someone type this'. The reflexive answer for years was to write the types by hand: open a Type.ts file, define interfaces, mirror the JSON keys, guess the types of the optional fields, and hope nothing was missed. The tedium of doing this for a wide API surface — dozens of endpoints, each with its own response shape — drove the development of automatic JSON-to-TypeScript generators, which take a sample document and produce a starting type definition that can be refined by hand. This tool is one of those generators, with the design choices and configuration knobs tuned for the cases that come up most often in real production code.
The generator works by walking the JSON tree, inferring a type for each value based on its runtime shape, and assembling those types into named interfaces (or type aliases — your choice). Primitives are obvious: "hello" → string, 42 → number, true → boolean, null → null (or, if the field is also seen with a real value elsewhere, the field is typed as `T | null`). Arrays are typed by inspecting their elements: an array of all strings is `string[]`, an array of mixed strings and numbers is `(string | number)[]`, and an empty array becomes `unknown[]` (because there is no evidence to infer a tighter type from). Objects are the interesting case: the generator gives each nested object a generated name (UserAddress, OrderLineItem, etc.) and emits it as a separate interface, which keeps the output flat and idiomatic instead of producing one giant nested type that no one can read.
Optionality detection is one of the trickier judgment calls. If a field appears in every sampled object, it is required; if a field is missing from at least one object, it is optional (`field?: type`). For a JSON sample with a single object, the generator has no information about which fields are sometimes-missing, so it marks everything as required by default — paste an array of multiple sample objects (or a few requests' worth of responses) to get better optionality detection. The 'always optional' mode is also available when you know the API in question has loose optionality and you want to be conservative; it marks every field as optional regardless of the sample, which is sometimes the right call for fields that come from a flexible third-party source.
Naming is another judgment call. The generator picks names from the JSON path — root.users[0].address becomes Address — but the same nested shape can appear at multiple paths, and the question of whether to merge them into one shared interface or duplicate them under different names is configurable. The default 'merge by structure' mode is usually right: if two nested objects have the same keys with the same types, they get the same generated interface. The 'merge by parent key' mode treats them as the same type only if they appeared under the same parent key (so users[0].address and orders[0].address share an Address but ship[0].address would be ShipAddress). Both modes have their place; the merge-by-structure default produces less code and is closer to how most hand-written TypeScript looks.
Unions in the generated output are how the tool handles fields that appear with multiple types across samples. If the JSON sample has `[{ id: 1 }, { id: "abc" }]`, the generated `id` field becomes `number | string`. This is usually a sign of a real polymorphic field in the source data, and the union is the right thing to surface — if it is actually a bug in the API and the field should always be one type, that is something you want to know. Discriminated unions (where the type of one field tells you the type of others) are not auto-detected by the generator because it would need cross-field correlation analysis, but the output is structured so that you can refactor toward a discriminated union by hand once you have the basic types in place.
The output customization is where this tool earns its weight against simpler generators. You can choose between `interface User { ... }` and `type User = { ... }` syntax (interface is the more common idiomatic choice for object shapes and supports declaration merging; type alias is required for unions, intersections, and certain advanced patterns). You can add `readonly` modifiers to every property if your codebase favors immutability. You can pick the casing of the generated names (PascalCase by default, configurable). You can drop or include the `export` keyword based on whether you intend to export the types from a module file or paste them into an existing file. You can name the root type explicitly so it does not get the generic 'Root' or 'GeneratedType' name. Each of these knobs corresponds to a real preference that varies across codebases.
A few real cases where this tool shines. First, exploring a new third-party API: paste a single response, get a starting type definition that you can refine while you are figuring out the API surface. Second, retrofitting types onto existing untyped code: an old codebase that talked to JSON without types, where you want to bolt on types as you read each function — paste a representative response, generate the type, replace the `any` in the function signature with the generated type, fix the type errors that surface as you do. Third, generating fixture types for tests: the JSON in your test fixtures probably wants to be typed for the test setup to be ergonomic, and generating the types from the fixture JSON is a one-step way to get there. Fourth, reverse-engineering a schema from an API that does not provide one: collect a handful of representative responses, paste them in, refine the result into a contract that you can use as documentation.
The generator handles a few edge cases that simpler tools miss. Empty objects (`{}`) are typed as `Record<string, never>` rather than the meaningless `{}` (which is TypeScript's 'anything except null/undefined' type and is almost never what you want). Empty arrays in the sample are typed as `unknown[]` to force the caller to decide how to narrow them, rather than silently typing them as `any[]` which would defeat the point of using TypeScript. Numbers that look like enums (a small set of distinct integer values) are not auto-converted to literal-type unions because the generator cannot tell from a sample whether those are the only allowed values or just the ones that happened to be in the sample; if you want literal types, edit the output by hand, since it is one find-and-replace away. Date strings are typed as `string` by default — if you want them as `Date`, the convention is to type them as `string` in the API boundary type and parse them at the deserialization layer; that is the right pattern for most TypeScript codebases.
Performance is straightforward: the generator runs in milliseconds for typical JSON samples (a few hundred kilobytes), and even a multi-megabyte sample with thousands of nested objects completes in well under a second. The output is rendered with syntax highlighting and a one-click copy button. There is no upload step — the JSON you paste is processed entirely in your browser by the generator running locally, and nothing is sent to any server. This privacy property matters when the JSON you are typing is real production data with secrets or PII; many online JSON-to-TypeScript generators upload the JSON to a server for processing, which is often not allowed by corporate security policies.
The tool pairs naturally with the rest of Omnvert's developer utilities. After generating types you might paste the JSON into the JSON Viewer to navigate the structure visually, or run two versions of the JSON through the JSON Diff tool to see how the schema evolved between API versions. The YAML ↔ JSON converter is helpful when the source format is YAML and you want TypeScript types from it (convert to JSON first, then run through this tool). The Regex Tester is useful for the post-generation cleanup step where you want to bulk-edit the generated names — for instance, replacing a generated `Root` with your project's preferred naming convention.
A common follow-on question: should I use this tool's output as my source of truth, or should I generate types from a schema that the API itself publishes? If the API publishes an OpenAPI spec, a JSON Schema, a GraphQL schema, or a protobuf definition, those are better sources of truth — the schema describes the contract, while a sample only describes one snapshot of one response. Tools like openapi-typescript, json-schema-to-typescript, graphql-codegen, and ts-proto are the right fit for those cases. This tool is for the common case where there is no published schema and the JSON sample is what you have to work with, or for fast prototyping where you want a starting type definition right now and will refine it later. Both workflows have their place; this tool serves the second one.
One subtle thing about how the generator handles inheritance: TypeScript's structural typing means that two different generated interfaces with the same shape are interchangeable from the type system's perspective, even though they have different names. The generator preserves the names you would expect (an Address inside an Order is named Address, not OrderAddress) but does not collapse 'looks like the same shape' into 'is the same name' across the entire output unless you explicitly opt in. This is intentional: keeping distinct paths as distinct names makes refactors easier later (renaming OrderAddress to BillingAddress is a one-line change if it is its own type, but a multi-file refactor if it has been merged into a generic Address used everywhere). If you prefer the merged style, the 'merge by structure' option does exactly that.
- Generate a starting type from an unfamiliar third-party API response.
- Bolt TypeScript types onto an old untyped codebase that talks to JSON.
- Type test fixtures so test setup is ergonomic.
- Reverse-engineer a contract from a handful of sampled responses.
- Compare auto-generated types vs your hand-typed versions to spot mistakes.
- Generate a quick starting interface during a code review for a new endpoint.
- Translate a webhook payload into a type while wiring up a webhook handler.
- Teach junior developers what idiomatic TypeScript object types look like.
- 1Paste a JSON sample (an object, an array of objects, or any nested structure).
- 2Pick a root type name and choose interface vs type alias output.
- 3Toggle readonly, optional handling, and union merging based on your codebase style.
- 4Watch the TypeScript output update live as you tweak options.
- 5Click Copy to grab the result, or Download .ts for a file you can drop into a project.
- 6Refine the output by hand for discriminated unions, literal types, or branded types.