JSON to TypeScript, Pydantic & Go
Paste a JSON sample and get TypeScript interfaces, Pydantic v2 models, Go structs or a JSON Schema. An array of objects folds into one type, with optional and nullable fields worked out from the rows.
Same thing, as an API
curl -X POST 'https://akifakkaya.com/api/v1/tools/json/types' \
-H 'Content-Type: application/json' \
-d '{"text":"[\n {\n \"id\": \"8f1b2c3d-4e5f-4a6b-8c9d-0e1f2a3b4c5d\",\n \"email\": \"ada@example.com\",\n \"created_at\": \"2024-03-01T10:00:00Z\",\n \"credits\": 120,\n \"plan\": { \"name\": \"pro\", \"seats\": 5 },\n \"labels\": [\"beta\"]\n },\n {\n \"id\": \"9a2c3d4e-5f6a-4b7c-8d9e-1f2a3b4c5d6e\",\n \"email\": \"grace@example.com\",\n \"created_at\": \"2024-04-12T08:30:00Z\",\n \"credits\": 99.5,\n \"plan\": { \"name\": \"free\", \"seats\": 1 },\n \"labels\": [],\n \"referred_by\": null\n }\n]","target":"typescript","root_name":"User"}'Free, no key, 120 requests a minute. Full endpoint reference
Generated types
Fix the JSON above to generate types.
How the shape was decided
- Arrays of objects fold into one type. Every element is merged, so ten rows describe the shape better than one.
- Missing keys become optional. A key absent from any element is marked optional rather than required.
- Nulls become nullable. A key that is null in one row and a string in another is a nullable string.
- Numbers widen. An integer in one row and a decimal in another gives a float.
About this tool
Typing a third-party response by hand is a transcription job, and transcription jobs get things wrong quietly. Paste the body you actually received and the shape comes back as code: nested objects become their own named types, and the outermost one takes whatever name you give it.
The part worth pasting a whole array for is the folding. Given a list of rows, the generator merges every element into one type instead of emitting a type per row: a key that appears in some rows and not others comes out optional, a key that is sometimes null comes out nullable, and a field that is an integer in one row and a decimal in another widens to a float. One well-chosen sample of ten rows produces a better type than one row ever can.
Strings that look like a timestamp or a UUID are narrowed where the target has a type for it — Pydantic gets datetime and UUID, Go gets time.Time, JSON Schema gets format: date-time. TypeScript keeps string, because that is what JSON.parse hands you at runtime and pretending otherwise moves the bug rather than fixing it.
Questions
- Which targets are supported?
- TypeScript interfaces, Pydantic v2 models, Go structs with JSON tags, and JSON Schema (draft 2020-12). The same inferred shape drives all four.
- How are optional and required fields decided?
- By what the sample shows. In an array of objects, a key present in every element is required; a key missing from any element is optional. A key whose value is null somewhere becomes nullable. That is why a multi-row sample is worth pasting.
- Does the Pydantic output run as-is?
- Yes. Nested models are emitted before the models that reference them, so there are no forward references to resolve, and optional fields get both a nullable annotation and a default — `str | None = None` rather than the `str = None` that Pydantic v2 rejects.
- What happens to keys that are not valid identifiers?
- TypeScript quotes them. Pydantic converts them to snake_case and adds a `Field(alias=...)` so the original key still parses. Go renames them and keeps the JSON tag.