WW Tools

JSON to TypeScript, Go & Zod Converter

Paste one JSON sample or many and get TypeScript interfaces, Go structs, or Zod schemas, with sample counts behind the optional fields.

TypeScript interfaces appear here

About JSON to TypeScript, Go & Zod Converter

A JSON to TypeScript converter saves you from typing out a shape you already have in front of you. The response is sitting in the Network tab, the repo has no interface for it, and hand-writing one for forty keys costs ten minutes you did not plan to spend. Paste the sample here and a TypeScript interface, a Go struct, or a Zod schema appears as you type. One sample tells you the shape. It does not tell you which keys are optional: a key that is absent from one response looks exactly like a key that does not exist, so most generators mark everything required and you find out in production. Paste several samples instead. A root-level JSON array, an NDJSON log with one record per line, and a few documents one after another all work. They are merged into a single type, and the panel above the output reports the counts behind the fields it marked optional, in the form "present in 2 of 3 samples" or "string in 2, null in 1". How optionality gets written is your call. Accurate mode keeps ? and | null separate, because a missing key and a null value are different runtime facts. Optional mode collapses both into ?. Nullable mode collapses both into | null. The same setting drives the Go output (a pointer field, or omitempty in the json tag) and the Zod output (.optional(), .nullable(), or .nullish()). Inference runs on this page. A response carrying customer records, internal ids, or a bearer token stays in the browser tab you pasted it into.

How to use the JSON to TypeScript converter

  1. Paste a JSON response into the input panel, or drop a .json, .ndjson, or .jsonl file onto it.
  2. To get optional fields right, paste more than one sample: a root-level JSON array, an NDJSON log with one record per line, or several documents in a row. The input selector sits on Auto and works out which one you pasted. The merged sample count appears above the output.
  3. Pick a target. TypeScript emits interfaces, or type aliases if you prefer, ready to paste next to your fetch call.
  4. Set the root type name. Rename any nested type in place and every reference to it follows, so nested objects never need fixing up by hand. A name with spaces or punctuation is normalized to a legal identifier, and the tool shows you the name it will emit.
  5. For Go, decide whether optional fields carry omitempty, whether nullable fields become pointers, and whether json tags keep the original key. Set the package name for the file you are pasting into.
  6. For Zod, choose a plain, strict, or passthrough object, and whether to export the matching z.infer type next to each schema.
  7. Read the review panel before you paste the result anywhere. It lists what one sample cannot settle: empty arrays, mixed arrays, arrays of arrays, fields that were only ever null, and integers too large for a JavaScript number. Then copy the output, or copy the same model as another target.

Common Use Cases

Type an undocumented API response

Copy the response out of DevTools, or rebuild the request with the cURL converter first, then paste it here instead of writing the interface by hand. This is the JSON to TypeScript interface generator case: a 40-key payload becomes a named interface, with nested objects and arrays of objects split into types of their own.

Get optional fields right from a log

One sample makes every field look required. Paste an NDJSON log or a page of results and the fields missing from some records come back optional, with the counts that justify the mark. Merging multiple JSON samples is the only way the converter can tell an optional key from a required one.

Model a third-party feed in Go

Emit a struct whose json tags carry the original keys, with omitempty on the fields that are genuinely optional and pointers only where a null actually turned up. Integers map to int64 and fractional numbers to float64. A key like user_name becomes an exported UserName field that still round-trips through its tag.

Put a Zod boundary on a fetch

Generate a schema plus its z.infer type, so the response is checked at runtime and the static type comes from the schema instead of being declared twice. Optional and nullable keys come out as .optional(), .nullable(), or .nullish() based on what the samples contained.

Frequently Asked Questions

Why are my generated optional fields wrong?

Because most generators only ever see one sample. Inside a single response, a key that happens to be absent is indistinguishable from a key that does not exist, so the safest thing the generator can do is mark everything required. Show it more than one sample and the ambiguity goes away: paste a root array, an NDJSON log, or several documents, and any key missing from some of them is marked optional. The panel above the output prints the evidence, for example "present in 2 of 3 samples", so you can check the result against the API instead of taking it on faith.

What is the difference between field?: T and field: T | null?

They are different runtime facts. The question mark means the key may be absent from the object, so "key" in obj is false and Object.keys does not list it. The union with null means the key is there and its value is null. In Go the same split is omitempty in the json tag (the key may be missing) versus a pointer type such as *string (the value may be nil). In Zod it is .optional() versus .nullable(), with .nullish() for a key that is both. Accurate mode keeps the two apart; the other two modes collapse them if your codebase prefers one style.

Can I generate types from more than one JSON sample at once?

Yes. A root-level JSON array, NDJSON with one record per line, and several documents pasted in a row all work, and Auto detection tells them apart. Every sample is merged into one type: keys are unioned, a key seen as a string in some samples and a number in others becomes a union, and a key missing from some samples is marked optional with its counts shown. Going from NDJSON to TypeScript this way gives you types that match a whole log file rather than its first line.

Should I generate a type or an interface?

For a plain JSON response either one works. Interfaces support declaration merging and tend to read better in editor tooltips for object shapes. Type aliases can express unions, intersections, and mapped or conditional types that an interface cannot, which starts to matter once you edit the generated shape by hand. Switch the Declaration control to see both.

What happens to empty arrays, mixed arrays, odd key names, and very large integers?

An empty array gives no element type, so it becomes unknown[], []any, or z.array(z.unknown()) and is listed in the review panel. A mixed array becomes a union of what was in it rather than taking the first element's type. An integer past Number.MAX_SAFE_INTEGER is called out, because JSON.parse has already rounded the value before any tool sees it. A key that is not a bare identifier is quoted in TypeScript and Zod. In Go it becomes an exported field with the original key kept in the json tag, and if two keys reduce to the same Go field name the second gets a numeric suffix so the struct still compiles.

Should I use a Zod schema instead of a TypeScript interface?

An interface is erased when TypeScript compiles, so it states what you expect and checks nothing at runtime. A Zod schema checks the data as it arrives and hands you the static type through z.infer, so the two stay in step. Use a schema at a boundary you do not control, such as a third-party API or a webhook body. An interface is enough for data you already trust.