# Stop Hand-Writing TypeScript Interfaces for API Responses

Every frontend developer knows this ritual: you call a new API, get back a JSON blob with 40 fields, and then spend the next 20 minutes hand-writing an interface for it. Miss one nested object and TypeScript complains. Guess a field type wrong and you ship a bug.

There is a better way: infer the types straight from the JSON.

## The problem with hand-written interfaces

Take a typical API response:

```json
{
  "id": 42,
  "title": "Hello world",
  "published": true,
  "tags": ["webdev", "typescript"],
  "author": {
    "name": "Jane",
    "followers": 1200
  }
}
```

Hand-writing the interface is not hard — it is just tedious and error-prone:

```typescript
interface Article {
  id: number;
  title: string;
  published: boolean;
  tags: string[];
  author: Author;
}

interface Author {
  name: string;
  followers: number;
}
```

Now imagine that response has six levels of nesting, arrays of objects, and a few nullable fields. This is where hand-writing stops being "quick" and starts being a source of bugs.

## Generate the types instead

The reliable approach is mechanical: parse the JSON, walk its structure recursively, and emit a type for every value.

- `string`, `number`, `boolean` map directly
- `null` stays `null`
- Arrays infer from their elements — `string[]` for `["a", "b"]`
- Arrays of objects get their own named interface, e.g. `ArticleItem`
- Mixed arrays become unions: `(number | string)[]`
- Empty arrays fall back to `any[]` rather than guessing wrong

A [free browser-based JSON to TypeScript converter](https://trencada.com/tools/json-to-typescript-converter/) does exactly this: paste the API response, name your root type, and copy the interfaces. It also handles the options you would otherwise set by hand — interface vs. type-alias style, optional properties, and the `export` keyword.

If you are working with a GraphQL codebase instead, the same idea applies: a [JSON to GraphQL converter](https://trencada.com/tools/json-to-graphql-converter/) turns a sample payload into SDL type definitions, which is handy when sketching a schema from a REST response you are migrating.

## Edge cases worth knowing

**Nullable fields.** JSON has `null` but no "optional" concept. If a field is `null` in your sample, mark it optional or widen it to `string | null` — whichever matches your API contract.

**Inconsistent arrays.** Real-world APIs sometimes return `[1, "2"]`. A union type `(number | string)[]` is honest; silently picking one type is not.

**Root arrays.** When the response itself is an array, you still want a named root: `export type Root = Item[];` keeps imports clean.

**Dates.** JSON has no date type — `"2026-09-26"` is a string. If your codebase uses `Date`, do a second pass and swap those fields.

## A workflow that actually sticks

1. Hit the endpoint, copy the raw JSON response.
2. Validate and format it first so you are converting clean data.
3. Generate the TypeScript interfaces from the JSON.
4. Review the output — rename the auto-generated nested types (`RootAuthor` → `Author`) to match your domain language.
5. Paste into your project and let `tsc` do the rest.

The review step matters. Generated types are a starting point, not a contract — your API's documentation (or its OpenAPI spec, if it has one) is the source of truth for which fields are truly optional.

## Why this beats the alternatives

- **Faster than hand-writing** for anything beyond a flat object.
- **More accurate** — the machine reads the actual values, not your assumptions.
- **Repeatable** — when the API adds a field, regenerate instead of diffing by hand.

Next time you integrate an API, skip the manual interface. Generate it, review it, ship it.
