JSON to Type Definition Generator
Instantly generate type definitions from a JSON sample for TypeScript, Python, Java, Go, and Kotlin.
What is the JSON to Type Definition Generator?
Manually writing type definitions for API responses is a chore. A single typo in a large JSON object can lead to bugs, and nested structures mean creating multiple classes. This generator streamlines the process: paste a sample JSON response, and it instantly creates complete type definitions for TypeScript, Python, Java, Go, and Kotlin on a single screen. It intelligently handles nested objects, infers optional properties from array samples, and creates union types for mixed-value fields. Since all processing happens in your browser, your data is never sent to a server.
How to use
- Paste your API response into the `JSON Input` box, or drag and drop a `.json` file.
- Select your desired language from the `TypeScript`, `Python`, `Java`, `Go`, or `Kotlin` tabs.
- Customize the `Root Name` to something descriptive, like `OrderResponse`.
- Adjust options like `Infer optional properties (?)` and `Allow null notation (| null)` to match your needs.
- Click `Copy` or `Save` to use the generated types in your project.
JSON to Type Definition Generator guide
How this tool is used in real work, and what to watch out for.
What Happens When You Transcribe Types by Hand
When you're manually typing out an `interface` for an API response with thirty keys, two things are guaranteed to happen. The first is a typo (like `created_at` becoming `create_at`), which is the lesser evil since the compiler will catch it. The second is the real problem: laziness, leading you to either use `any` or only declare the fields you need right now.
At that moment, the type definition stops being a representation of the "response's shape" and becomes a "list of fields I'm currently using." The next person to work on it won't find an existing field in autocomplete and will add it again as a duplicate. This tool breaks that cycle by letting you paste in a real, complete response to generate the entire shape at once.
The Limits of Inferring Optional Properties (?) โ This Is Crucial
This tool has only one way of determining if a property is optional: if an array contains multiple objects of the same shape, any key that exists in some elements but not others is marked with a question mark. There is no other basis for this inference.
Conversely, this means the tool knows nothing about what's not in your sample. If a field is nullable in the API spec but happened to have a value in every response you provided, the tool will generate it as required and non-null. The code will compile and run perfectly, right up until the day a response with a missing value arrives and your app crashes at runtime.
- Here's the most practical tip: don't paste a single object, paste the entire list response. If the `items` array contains 20 objects, the tool can compare all 20 to infer optional properties and union types far more accurately.
- An even better approach is to combine several different kinds of responses into a single array for the input. For example, you could include a normal case, a case without a coupon, and a cancelled order all together.
- Disabling "Infer optional properties (?)" forces all keys to be generated as required. Use this when you're certain about the API spec and don't want the tool's guesswork.
| Sample You Provide | Generated Type | The Reality |
|---|---|---|
| A single response where `coupon` is `null` | `null` or `any` | The field might actually contain a coupon object. You have to fix this manually. |
| A single response where `tags` is an empty array | `any[]` | The element type is unknown. You need a sample that contains elements. |
| Just one object from a list response | All keys are marked as required | The API spec likely has optional fields mixed in. |
| A single item with `status: "paid"` | `string` | It's probably a union type like `"paid" | "shipped" | "cancelled"`. |
| A single item where `price` is `30000` | `number` / `int` / `int64` | The field might need to accommodate floating-point numbers. |
The Illusion of Safety with Types
TypeScript types only exist at compile time. They are erased during the build process. Casting a fetched JSON object with `as OrderResponse` is just a "promise to the compiler" that the object will have that shape; it doesn't actually check that it does.
This is why TypeScript won't say a word if the server changes a field name or starts sending `null` for a non-nullable field. You'll only find out when your UI crashes trying to read `undefined`. The danger is that the more precise a type this tool generates, the greater the false sense of "validation" it can create.
- At the boundaries of your application (i.e., where you receive external API responses), use a runtime validation library like Zod or Valibot to parse the data. You can use the `interface` generated by this tool as a starting point for writing your schema.
- For data that only circulates internally (like configuration files or constants), the output of this tool is usually safe to use as-is.
- In Java, Kotlin, and Go, deserialization will generally throw an exception if the incoming data doesn't match the type definition. This is fundamentally different from TypeScript, where type mismatches can pass through silently.
Language-Specific Output
If a JSON key is in snake_case, like `order_id`, the tool will generate a field name that follows the language's convention, such as `orderId` or `OrderID`. Since this name change breaks the automatic mapping, an annotation or tag is added to preserve the original JSON key. If you use a different serialization library (e.g., Gson, Moshi), you may need to replace the generated annotation with the one your library uses.
Number types are inferred based on the values observed. If all numbers for a field are integers, it will be generated as `int`, `long`, or `int64`. If even one is a float, it will be `float` or `double`. If a `price` field contained only integers in your sample, double-check if it can actually contain decimals.
| Language | Flavor | For Name Mismatches | Notes |
|---|---|---|---|
| TypeScript | `interface` / `type` alias | โ | All options (`readonly`, `export`) are meaningful. |
| Python | `dataclass` / `TypedDict` | โ | `dataclass` can be made immutable with `frozen=True`. `TypedDict` is for when you want to treat the data as a plain `dict`. |
| Java | `record` | `@JsonProperty` (Jackson) | `record` types are already immutable, so the `Readonly` option is disabled. |
| Go | `struct` | `json` tag | Enabling nullability will generate pointer types. |
| Kotlin | `data class` | `@SerialName` (kotlinx.serialization) | Nullability is indicated with a `?`. |
5-Minute Checklist Before Pasting
- Rename the root type and any nested types to match your project's coding conventions. An element in an `items` array will be inferred as `Item`โrename it if the name is too generic.
- For fields inferred as `string` that have a fixed set of possible values (e.g., `status`, `type`, `grade`), narrow the type to a union or an enum.
- ISO date strings are inferred as `string`. Decide whether to convert them to `Date` objects or keep them as strings, according to your team's rules.
- Be careful with large integers (order numbers, Snowflake IDs) inferred as `number`. JavaScript's `number` type loses precision for integers larger than 2^53. Check if the server can provide these as strings, and change the type to `string` for safety.
- Compare the generated types against the official API documentation. Manually add any fields that are in the spec but were missing from your sample.
Frequently asked questions
Is the JSON I enter sent to your server?
No. All parsing and type generation happens entirely in your browser. Nothing is transmitted or stored, making it safe to use with real API data.
How does it handle nested objects and arrays?
Nested objects become separate types, named after their key (e.g., a `customer` object becomes a `Customer` type). For arrays, it analyzes all elements to infer a common item type.
What makes a property optional?
When analyzing an array of objects, any key that exists in some objects but not others is marked as optional. For this to work, your sample must be an array with multiple objects.
Why are `json` tags or annotations like `@JsonProperty` added?
When a JSON key (e.g., `order_id`) is renamed to fit code style (`orderId`), these maintain the link for serialization. They are added for Go, Java, and Kotlin.