Why Type Your API Responses?
Untyped API responses are the most common source of runtime errors in TypeScript applications. Adding types to your API layer gives you:
- Autocompletion for response fields in your IDE
- Compile-time errors when you access a field that doesn't exist
- Documentation that stays in sync with your code
- Refactoring safety when the API structure changes
Basic JSON to TypeScript Mapping
// JSON
{
"id": 42,
"name": "Jane Doe",
"email": "jane@example.com",
"active": true,
"score": 3.14,
"tags": ["admin", "user"]
}
// TypeScript Interface
interface User {
id: number;
name: string;
email: string;
active: boolean;
score: number;
tags: string[];
}
Nested Objects
// JSON
{
"user": {
"id": 42,
"address": {
"street": "123 Main St",
"city": "Springfield",
"zip": "12345"
}
}
}
// TypeScript — define nested types separately
interface Address {
street: string;
city: string;
zip: string;
}
interface User {
id: number;
address: Address;
}
interface ApiResponse {
user: User;
}
Arrays of Objects
// JSON
{
"items": [
{ "id": 1, "name": "Widget", "price": 9.99 },
{ "id": 2, "name": "Gadget", "price": 24.99 }
],
"total": 34.98
}
// TypeScript
interface CartItem {
id: number;
name: string;
price: number;
}
interface Cart {
items: CartItem[];
total: number;
}
Optional and Nullable Fields
Real APIs often include null values or omit fields entirely. Handle them precisely:
// JSON — avatar may be null, bio may be absent
{
"id": 42,
"name": "Jane",
"avatar": null,
"bio": "Developer"
}
// TypeScript
interface User {
id: number;
name: string;
avatar: string | null; // present but may be null
bio?: string; // may not be present at all
}
Union Types from Discriminated Fields
// API returns different shapes based on "type"
{ "type": "text", "content": "Hello" }
{ "type": "image", "url": "https://...", "alt": "Photo" }
// TypeScript discriminated union
type TextBlock = {
type: "text";
content: string;
};
type ImageBlock = {
type: "image";
url: string;
alt: string;
};
type Block = TextBlock | ImageBlock;
TypeScript's control flow analysis will narrow the type automatically after checking block.type.
Handling Unknown API Shapes: Zod
TypeScript types are erased at runtime. If the API sends unexpected data, your types won't catch it. Use a runtime validation library to parse and validate:
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
tags: z.array(z.string()),
});
type User = z.infer<typeof UserSchema>; // TypeScript type from Zod schema
const user = UserSchema.parse(apiResponse); // throws if invalid
Generating Types from OpenAPI / Swagger
If your backend has an OpenAPI specification, generate TypeScript types automatically rather than writing them by hand:
# openapi-typescript
npx openapi-typescript https://api.example.com/openapi.json --output src/types/api.ts
# @hey-api/openapi-ts (generates a full typed client)
npx @hey-api/openapi-ts -i openapi.json -o src/api
Convert JSON to Types Instantly
Paste any JSON into ToolsVito's JSON to Code tool to generate TypeScript interfaces, Go structs, or Zod schemas — directly in your browser, nothing uploaded.