How to use JSON → TS
- 1. Start from real JSON. Paste a representative response, not a single happy-path object. Arrays of objects produce better optional-field inference.
- 2. Name the root type. Set a root name such as User or CheckoutSession. Nested objects are named from keys.
- 3. Pick the emit target. Interfaces are best for public models. Types are better for unions. Zod is best when you need runtime validation.
- 4. Copy into your project. Treat the output as a starting point. Tighten number ranges, string unions, and branded IDs by hand.
About this tool
Most TypeScript services begin with a JSON example from an API. Hand-writing interfaces is slow and drifts from production. Inferring types from a sample is the fastest way to get a compiling client, as long as you remember that a sample is not a contract. This tool walks the JSON tree, merges objects that appear together in arrays, and emits interfaces, type aliases, or Zod schemas you can paste into a repo.
Inference is not a schema
If every user in your sample has an `email` string, the generated type will require `email: string`. The next response from production might omit it. Arrays are the best signal for optionality: if a field appears on some objects and not others, PureDevKit marks it optional. Null and a string together become a union. That is still only as good as the sample. For a real contract, prefer OpenAPI, JSON Schema, or a server-shared Zod module. Generated types should be reviewed like any other diff.
Interfaces vs type aliases vs Zod
TypeScript interfaces can be merged and are the usual choice for object shapes. Type aliases are required for unions and primitives and are often clearer for DTOs that are not objects. Zod goes one step further: it validates at runtime, which is the only way to be safe at a trust boundary (an HTTP handler, a `localStorage` read, a query string). `z.infer<typeof Schema>` then gives you the TypeScript type for free. Use Zod (or similar) whenever data enters your process from the outside world.
Naming and nested objects
Nested objects become named types so you can reuse them. Names are derived from the root you provide and from object keys, passed through PascalCase. Collisions get a numeric suffix. JSON keys that are not valid identifiers are quoted (`"content-type": string`). That output compiles, but you may still want to map wire names to camelCase with a serializer such as `zod` `transform` or a tRPC transformer.
What this generator will not do
It will not detect ISO date strings as `Date` (they stay `string`, which is usually correct on the wire). It will not emit branded IDs, integer vs float distinctions, or tuple types for mixed arrays beyond unions. It will not follow `$ref` in JSON Schema. Those constraints keep the output predictable. After you paste the result, replace `string` with string-literal unions for enums you know, and replace `number` with branded types where IDs must not mix.
Code examples
Zod parse
const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
});
const user = UserSchema.parse(JSON.parse(raw));Fetch client
const res = await fetch("/api/user");
const json: unknown = await res.json();
const user = UserSchema.parse(json);Python TypedDict
from typing import TypedDict
class User(TypedDict):
id: str
email: str