JSON to Types
Paste a JSON sample and get types in whichever of nine languages you need. An array of objects is merged into one type rather than typed from the first element, a field missing from some elements becomes optional, a field that is null somewhere becomes nullable, and nested objects get their own named types.
Try an example loads a two-element array where the elements deliberately disagree, which is where the inference earns its keep.
The inference is the work; the printing is nine easy jobs
Most tools in this space do TypeScript and stop. The reason is not that the other eight languages are hard, it is that the shape inference is the whole of the difficulty and it is easy to under-build. Get it right once and every emitter after the first is an afternoon.
These are the rules, and each one exists because real API responses break the naive version:
- An array of objects is merged into one type. Typing from the first element is the default failure. If element one has an
emailand element two does not, the first-element approach gives you a type that lies about the second. - A field missing from some elements becomes optional. That is the direct consequence of merging, and it is the single most useful thing this page tells you about a sample.
- A field that is null in some elements becomes nullable. Distinct from optional, and the distinction matters in every language here:
string | nullis notstring | undefined. - An integer and a float together are a float.
240and19.99in the same field is anumber, afloat, anf64. Going the other way would silently truncate. - Identical shapes reuse one type. Two nested objects with the same fields do not produce two identical interfaces with a numeric suffix.
- Nested types are named after their key, de-pluralized for array elements. A key called
ordersholding an array of objects produces a type calledOrder, notOrdersorRoot2.
Worked example: two elements that disagree
The sample is an array of two users. Read the differences between them before reading the output:
[
{ "id": 1, "first_name": "Ada", "email": "ada@example.com",
"active": true, "balance": 240.5,
"address": { "city": "London", "postcode": "NW1 4RY" },
"tags": ["founder", "engineer"] },
{ "id": 2, "first_name": "Grace",
"active": false, "balance": 0,
"address": { "city": "New York", "postcode": "10011" },
"tags": [], "manager_id": null }
]
As TypeScript:
export interface Address {
city: string;
postcode: string;
}
export interface User {
id: number;
first_name: string;
email?: string;
active: boolean;
balance: number;
address: Address;
tags: string[];
manager_id?: unknown | null;
}
email is optional because element two lacks it. balance is a plain number because 240.5 and 0 unify to one. address became its own named interface. tags is string[] even though the second element's array is empty, because the first told us what is in it. And manager_id is both optional and nullable, which is the honest answer: the sample established it can be absent and it can be null, and established nothing at all about what it holds otherwise.
The same sample, in the other eight
Each emitter follows its own language's conventions rather than transliterating the TypeScript.
# Pydantic
class User(BaseModel):
id: int
first_name: str
email: Optional[str] = None
balance: float
// Go
type User struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
Email *string `json:"email,omitempty"`
Balance float64 `json:"balance"`
}
// Rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: i64,
pub first_name: String,
pub email: Option<String>,
pub balance: f64,
}
Go gets exported names with json tags carrying the original key and omitempty on the optional ones. Rust gets snake_case fields with a serde(rename) attribute wherever the JSON key differs. Pydantic gets snake_case with a Field(alias=…) where needed. Kotlin, Swift and C# get camelCase with their own mapping mechanism: Swift emits a CodingKeys enum, C# emits [JsonPropertyName] attributes. Zod emits a schema plus an inferred type, so you get validation and a type from one definition. Java emits a record.
Dates are reported, not guessed
A string that looks like a date is typed as a string, and a note above the result names the fields it noticed. That is deliberate, and it is worth explaining because several tools do the opposite.
Retyping "2026-09-03T10:14:00Z" as a Date, a datetime or a time.Time is a decision about your parser, not a fact about your data. In TypeScript, JSON.parse produces a string and typing it as Date makes the type wrong at runtime. In Go it needs a custom unmarshaller or a tagged time layout. In Pydantic it works, which is why Pydantic users expect it. Since the right answer differs per language and per codebase, the page tells you what it saw and leaves the call to you.
The options, and when to change them
- Root type name names the top-level type. Nested types are named from their keys regardless, so this only affects the outermost one.
- Missing keys are optional is on by default and is the merging behavior described above. Turn it off when your sample is exhaustive and you want every field required, which is the right call when the sample is a fixture rather than a sample of production traffic.
- Export / public controls the visibility keyword:
exportin TypeScript and Zod,publicin Java and C#,pubon Rust fields either way. - Readonly properties adds
readonlyin TypeScript and usesletrather thanvarin Swift. Worth turning on for a type describing an API response, which nothing should be mutating.
A larger sample gives a better result, always. Two elements that differ tell the inference more than two hundred identical ones, so the most useful thing you can paste is a page of real responses rather than one hand-picked record.
Frequently Asked Questions
Which languages are supported?
TypeScript interfaces, a Zod schema, a Pydantic model, a Go struct, a Rust struct with serde derives, a Kotlin data class, a Java record, a C# record and a Swift Codable struct. Nine, from one inference pass, so the shape decisions are identical across all of them.
Why is a field optional when it is present in my sample?
Because it is absent from at least one element of an array your sample contains. That is the merging rule doing its job. If your sample is exhaustive and you want every field required, turn off "Missing keys are optional".
What is the difference between optional and nullable in the output?
Optional means the key can be absent; nullable means the key is present with a null value. TypeScript spells them field?: T and field: T | null, Rust uses Option<T> for both but the serde attributes differ, and Kotlin, Swift and C# each have their own. A field that is both gets both.
Why was my date string not typed as a date?
Because retyping it is a decision about your parser rather than a fact about the data, and the right answer differs by language. The note above the result names every field holding a date-shaped string, so you can change the ones you want by hand.
How are nested types named?
After the key that holds them, in PascalCase, de-pluralized when the key holds an array. A key orders gives you Order, boxes gives Box, people gives Person. Two different shapes that want the same name get a numeric suffix; two identical shapes reuse one type.
Can I paste JSON that is not quite valid?
Yes. The tolerant reader is always on for this page, so a config file with comments, a Python dict with single quotes, or an object with unquoted keys all work, with a note saying what was forgiven. Getting types out of a sample somebody pasted into a chat window is exactly the case this exists for.
Does anything I paste leave my computer?
No. There is no upload endpoint on this page and no network request in the code that does the work. JavaScript in your own tab reads the text, processes it and hands back the result. Nothing is stored between visits either, so reloading gives you an empty box again. You can confirm it by opening your browser's network panel and watching it stay quiet while you work.
A sample in, types out
Free, no account, no upload. Nine languages from one inference pass.
Back to the generator