# How to Convert JSON to TypeScript Interfaces (Generate Types Automatically)

URL: https://pdfflare.com/blog/convert-json-to-typescript-interfaces
Published: May 1, 2026
Reading time: 7 min read

> Convert JSON to TypeScript interfaces free online. Generate types from API responses — handles nested objects, mixed-type arrays, and optional fields.

---

You're wiring up a third-party API and the docs hand you a JSON example response — 200 lines deep, half a dozen nested objects, arrays inside arrays. You can either spend twenty minutes hand-typing the TypeScript interfaces and inevitably miss a field, or paste the JSON into a generator and get the types in two seconds.

In this guide you'll learn how to convert JSON to TypeScript interfaces automatically using [PDFFlare's JSON to TypeScript tool](https://pdfflare.com/tools/json/json-to-typescript) — how it handles nested shapes, mixed-type arrays, and optional fields, plus where the generator falls short and what to fix by hand.

## Why Generate TypeScript Types From JSON?

A typed API response unlocks four big wins:

- **Autocomplete in your editor:** Type `response.` and your IDE shows every field. No flipping back to the docs.
- **Compile-time safety:** Renaming a field? TypeScript flags every consumer. Mistyping `response.userid` instead of `response.userId`? The compiler catches it before runtime.
- **Refactor confidence:** Restructuring data flow with types is dramatically safer than without. The compiler is the test suite.
- **Self-documenting code:**The interface is the contract. Future-you (and teammates) doesn't need to read the API docs to understand the shape.

## How to Convert JSON to TypeScript Interfaces (Step by Step)

PDFFlare's generator runs entirely in your browser — your JSON (potentially containing API tokens, customer data, or production payloads) never uploads anywhere.

1. **Open [PDFFlare's JSON to TypeScript tool](https://pdfflare.com/tools/json/json-to-typescript)** — no signup.
2. **Paste a sample JSON response** into the input pane. One representative example is enough — the generator infers types from it.
3. **Click Convert.** The output pane shows clean TypeScript interfaces, one per nested shape, with sensible names like `RootObject`, `Address`, `OrderItem`.
4. **Copy and paste into your codebase.** Drop the output into a `types.ts` file and import where you need.
5. **Tweak the names** if you want — interfaces auto-named from key names; rename to match your codebase conventions.

## How the Generator Decides Types

The mapping from JSON to TypeScript is mostly mechanical, but a few rules are worth knowing:

- **JSON string** → TS `string`
- **JSON number** → TS `number` (TypeScript has no separate int / float)
- **JSON boolean** → TS `boolean`
- **JSON null** → TS `null` (you may want to widen to `T | null` by hand)
- **JSON array of T** → TS `T[]`
- **JSON object** → TS `interface` with a property per key

## How Mixed-Type Arrays Are Handled

A common API gotcha: an array contains items of different shapes. The generator detects this and emits a union type. For example, `[{ "kind": "user", "name": "alice" }, { "kind": "bot", "id": 7 }]` becomes `(User | Bot)[]`, with each shape getting its own interface. You then narrow on `kind` at the consumer.

## How Nested Objects Become Sub-Interfaces

Nested objects don't become inline types — they get their own named interface. So `{ "user": { "address": { "city": "NYC" } } }` generates three interfaces: `RootObject` containing a `user: User`, `User` containing an `address: Address`, and `Address` with `city: string`. Easy to import individually, easy to extend.

## Where the Generator Falls Short (Fix by Hand)

Type inference from a single example has limits. After generating, adjust these by hand:

### Optional Fields

A field that's present in your sample is typed as required. If the API can omit it, mark it optional with `?:`. For example, change `avatar: string;` to` avatar?: string;`.

### Nullable Fields

A field that's `null` in your sample becomes `null` in the type — but most fields that can be null can also be a real value. Widen to `T | null` manually:` avatar: string | null;`.

### String Literal Unions

Status fields like `"status": "pending"` become `string`. If the API returns a small, known set of values, tighten to a literal union:` status: "pending" | "active" | "closed";`.

### Empty Arrays

An empty array `[]` in the sample becomes `never[]`because there's nothing to infer from. Replace with the actual element type once you know it (e.g. `tags: string[]`).

### Maps With Dynamic Keys

If an object is really a map (keys are dynamic IDs, not a fixed schema), the generator emits one interface per known key. Replace with an index signature: `[id: string]: ItemValue`.

## A Worked Example: Typing a Real API Response

Suppose you're wiring up a webhook from a CRM. The example payload looks like this:

`{ "event": "contact.created", "data": { "id": 42, "email": "alice@example.com", "tags": ["lead","priority"], "owner": null }, "timestamp": "2026-05-01T08:00:00Z" }`

Pasted into the generator, you get something close to:

- `RootObject` with `event: string; data: Data; timestamp: string;`
- `Data` with `id: number; email: string; tags: string[]; owner: null;`

Three things are worth fixing by hand here:

1. **`event` is a discriminator.** The webhook sends a small known set of event names. Tighten to a literal union: `event: "contact.created" | "contact.updated" | "contact.deleted";`. Now your switch statement is exhaustive.
2. **`owner` is nullable, not always null.** Widen to `owner: User | null;` once you know the non-null shape. The current type makes it impossible to ever assign a real user.
3. **`timestamp`is a string, but it's an ISO 8601 date.** If you parse it to a `Date` on receipt, type the parsed shape: keep the wire type as `string` for the raw payload, then convert.

## Sample-Based vs Schema-Based Type Generation

PDFFlare's tool infers types from a single sample. There's a different approach: generate types from a JSON Schema (or OpenAPI) document. Trade-offs:

- **Sample-based:** Fast, zero-setup, works when you only have one example payload. The downside: required vs optional fields are guesswork, and you only see the shape that one sample happens to have.
- **Schema-based (e.g. quicktype, openapi-typescript):** Definitive — the schema declares which fields are optional, which are nullable, which have enum constraints. The downside: you need a schema, which not all APIs publish.

A pragmatic workflow: use sample-based generation as the starting point, then evolve toward schema-based if the API publishes a schema (or generate one yourself with [PDFFlare's JSON Schema tool](https://pdfflare.com/tools/json/json-schema)).

## Runtime Validation: Types Don't Help in Production

TypeScript erases all types at runtime. If the API returns malformed data (a missing field, a wrong type), your typed code happily proceeds and crashes somewhere downstream with a confusing error. The fix: add a runtime validator at every API boundary. Options:

- **JSON Schema validation**— generate a schema with PDFFlare's JSON Schema tool, validate at the boundary with AJV. Same source-of-truth for types and runtime checks.
- **Zod / io-ts** — define a single schema in TypeScript that produces both the type and a runtime parser. Slightly more ceremony than sample-based generation, but bulletproof.
- **Hand-written guards** — for tiny APIs, just` typeof`checks. Doesn't scale, but works.

## Common Scenarios

### Typing a Third-Party API

You're hitting a REST API and the docs only give a JSON example. Drop it in the generator, get an interface set, type your fetch wrapper. Done in under a minute. If you also want a runtime check, generate a JSON Schema with [PDFFlare's JSON Schema tool](https://pdfflare.com/tools/json/json-schema) and validate at the boundary.

### GraphQL Response Types

If you're on GraphQL, codegen tools like graphql-codegen give you types automatically. But for one-off queries or non-codegen workflows, paste a sample response into the JSON to TypeScript tool — fast and dependency-free.

### Local Storage and IndexedDB Schemas

When you read JSON back from localStorage or IndexedDB, you need a type for it. Generate from your write payload, save the interface, and now your reads are type-safe.

### Webhook Payload Types

Stripe, GitHub, Slack, and other webhook providers document payload shapes as JSON examples. Paste the example, generate the type, drop into your handler. Add the optional `?:` markers based on which fields the docs say are nullable.

## Best Practices

- **Use the most representative sample.** A response with all optional fields populated produces better types than a minimal one. Run a real query and use that.
- **Sort keys before generating.** Run the JSON through [PDFFlare's JSON Formatter](https://pdfflare.com/tools/json/json-formatter) with Sort Keys on. Stable ordering means stable interface ordering when you regenerate.
- **Validate at runtime, not just compile time.** Types are erased at runtime — bad data still gets through. Pair the interface with a JSON Schema or Zod validator at the API boundary.
- **Check the generator output.**Don't blindly paste — read the interfaces, mark optional fields, fix string literal unions, widen nulls.

## Naming Generated Interfaces Cleanly

The generator picks names like `RootObject`, `Data`, `Item`. Functional, but generic. Three quick rename patterns improve readability:

- **Name the root after the operation,** not the data.` GetUserResponse` is more useful than `RootObject` when you have several similar interfaces in one file.
- **Name nested types after the parent key.** A nested `address` object becomes `Address`; a nested `preferences` becomes `Preferences`. The generator does this most of the time.
- **Suffix array element types with their role.** An `items: Item[]` field is fine, but `items: OrderItem[]` tells the reader what kind of items at a glance.

## Generated Types vs Hand-Written Types

Generated types are a fast starting point, not the final answer. A few cases where hand-written wins:

- **Discriminated unions.** A polymorphic API response keyed by a discriminator (`kind`, `type`, `__typename`) is far more useful as a hand-written tagged union with literal types than as a generic union.
- **Branded primitives.** An `id` field is technically a `number`, but you don't want to accidentally pass an `orderId` where a `userId` is expected. A branded type (`type UserId = number & { __brand: "UserId" }`) catches this — the generator can't infer it.
- **Aliased imports for shared shapes.**If two endpoints return the same nested shape, generate once and import everywhere. Don't paste the same interface into multiple files.

## Privacy: Your JSON Stays on Your Device

PDFFlare's JSON to TypeScript tool runs entirely in your browser. The inference logic is JavaScript executed locally — your JSON never uploads to any server. Production payloads, API tokens, customer data are all safe to paste.

## Wrapping Up

Hand-typing TypeScript interfaces from a JSON example is a chore that a generator does in two seconds. Generate, then spend the saved time on the parts that need human judgment: optional fields, string literal unions, nullability. The result is a typed API integration that catches bugs at compile time instead of in production.

Got a JSON sample that needs types? Open [PDFFlare's JSON to TypeScript tool](https://pdfflare.com/tools/json/json-to-typescript) and you'll have interfaces in your editor before your coffee finishes brewing.

## Related Tools

- [JSON Schema](https://pdfflare.com/tools/json/json-schema) — generate a runtime validator from a sample
- [JSON to Go](https://pdfflare.com/tools/json/json-to-go) — generate Go structs the same way
- [JSON to Python](https://pdfflare.com/tools/json/json-to-python) — generate Python @dataclass types
- [JSON Formatter](https://pdfflare.com/tools/json/json-formatter) — format and sort JSON before generating

---

## Frequently asked questions

**Q: How does PDFFlare generate TypeScript types from a JSON sample?**

A: The generator parses your JSON into an AST, walks every node, and infers a TypeScript type at each level: strings → string, numbers → number, booleans → boolean, arrays → T[], objects → named interfaces. Nested objects get their own interface with a name derived from the parent key. Mixed-type arrays produce union types like (User | Bot)[].

**Q: Why are some fields typed as required when the API can omit them?**

A: The generator infers types from the single example you provide. If a field is present in your sample, it&apos;s marked required. If the API can omit it, edit the interface afterwards to add a ? — change avatar: string; to avatar?: string;. For more rigorous typing, generate a JSON Schema with PDFFlare&apos;s JSON Schema tool and use a schema-driven type generator like quicktype.

**Q: Will the generator handle nullable fields correctly?**

A: A field that&apos;s null in your sample is typed as null — but most fields that can be null can also be a real value. Widen by hand to T | null after generating. For example, change avatar: null; to avatar: string | null; once you know the non-null type.

**Q: Can I generate types for an array of mixed shapes?**

A: Yes — the generator detects per-item shape differences and emits a union. So [{kind:"user",name:"alice"},{kind:"bot",id:7}] becomes (User | Bot)[] with two interfaces. You can narrow on the discriminator field (kind in this example) at the consumer for type-safe branching.

**Q: Do I need to worry about my JSON being uploaded?**

A: No. PDFFlare&apos;s JSON to TypeScript tool runs entirely in your browser. The inference logic is JavaScript executed locally — your JSON, including any API tokens, customer data, or production payloads, never uploads to any server.

---

## About PDFFlare

PDFFlare is a free collection of online tools for working with PDFs, images, text, JSON, and developer utilities. All tools run client-side in your browser — no signup, no upload to our servers, no rate limits.

For the full site index, see https://pdfflare.com/llms.txt.
For the complete content dump in one file, see https://pdfflare.com/llms-full.txt.