Home β JSON to TypeScript Guide
JSON to TypeScript: Complete Guide with Examples (2026)
Converting JSON to TypeScript interfaces is one of the most common tasks when building typed APIs. Instead of manually writing interfaces, you can generate them automatically β saving time and eliminating typos. This guide covers everything: type mapping, nested objects, optional fields, arrays, generics, and best practices.
Try the Free Tool
Paste any JSON and get TypeScript interfaces instantly.
Convert JSON to TypeScript βWhat Is a TypeScript Interface?
A TypeScript interface defines the shape of an object β what properties it has and what types those properties are. When you fetch JSON from an API, TypeScript has no idea what shape the response has. By creating an interface that matches your JSON structure, you get:
- Type safety β TypeScript warns you at compile time if you access a property that doesn't exist
- Autocomplete β Your IDE knows every field and its type as you type
- Refactoring β Rename a property and TypeScript finds every usage instantly
- Documentation β Interfaces serve as living docs for your API response shapes
JSON to TypeScript Type Mapping
Every JSON value maps to a specific TypeScript type:
| JSON Value | JSON Type | TypeScript Type | Example |
|---|---|---|---|
"hello" | string | string | name: string |
42 | number | number | age: number |
3.14 | number | number | score: number |
true | boolean | boolean | isActive: boolean |
null | null | null | data: null |
["a","b"] | array | string[] | tags: string[] |
[1, 2, 3] | array | number[] | ids: number[] |
{...} | object | interface | address: Address |
[1, "a"] | mixed array | (number | string)[] | mixed: (number | string)[] |
Basic Example: Simple JSON to Interface
Start with a simple flat JSON object:
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"age": 28,
"isActive": true,
"score": 9.5,
"bio": null
}
The generated TypeScript interface:
interface User {
id: number;
name: string;
email: string;
age: number;
isActive: boolean;
score: number;
bio: null;
}
Tip: If bio can be either a string or null, type it as bio: string | null instead of just null.
Nested Objects
When JSON has nested objects, the generator creates separate interfaces for each nested structure and references them:
{
"user": {
"id": 1,
"name": "Alice",
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
}
}
}
Generated TypeScript:
interface Address {
street: string;
city: string;
zip: string;
}
interface User {
id: number;
name: string;
address: Address;
}
interface RootObject {
user: User;
}
Each nested object becomes its own named interface, making the code clean and reusable. The outer interface references the inner one by name.
Arrays of Objects
Arrays of objects are very common in API responses. Here's how they convert:
{
"users": [
{ "id": 1, "name": "Alice", "role": "admin" },
{ "id": 2, "name": "Bob", "role": "user" }
],
"total": 2
}
Generated TypeScript (the first array element is used to infer the item type):
interface UsersItem {
id: number;
name: string;
role: string;
}
interface RootObject {
users: UsersItem[];
total: number;
}
Optional Fields
JSON APIs often return fields that may or may not be present. Mark these with ? in TypeScript:
interface User {
id: number;
name: string;
email?: string; // may not be present
avatar?: string | null; // present but possibly null
metadata?: Record<string, unknown>;
}
The automated converter generates non-optional fields by default. After generating, review and add ? to any fields your API might omit.
Handling null Values Properly
There's an important distinction between null and undefined in TypeScript:
field: nullβ always null, never changesfield: string | nullβ can be a string or null (nullable)field?: stringβ may not exist on the object (optional)field?: string | nullβ optional AND nullable
When your JSON shows null for a field, the real-world type is usually string | null or number | null. The generator uses null as a conservative choice β update it to match your actual API contract.
Real-World API Example
Here's a realistic GitHub API user response converted to TypeScript:
{
"login": "octocat",
"id": 1,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"html_url": "https://github.com/octocat",
"public_repos": 2,
"public_gists": 1,
"followers": 20,
"following": 0,
"created_at": "2008-01-14T04:33:35Z",
"updated_at": "2008-01-14T04:33:35Z"
}
interface GitHubUser {
login: string;
id: number;
avatar_url: string;
html_url: string;
public_repos: number;
public_gists: number;
followers: number;
following: number;
created_at: string; // ISO date string β consider using Date
updated_at: string;
}
Best practice: ISO date strings like "2008-01-14T04:33:35Z" are typed as string by default. If you parse them with new Date(), you can refine the type to a string that you know represents a date, or create a type alias: type ISODateString = string.
interface vs type: Which Should You Use?
Both interface and type can describe object shapes. Here's when to use each:
Use interface when⦠| Use type when⦠|
|---|---|
You want to extend it with extends | You need union types: A | B |
| You want to implement it in a class | You need intersection types: A & B |
| Describing plain API response objects | You need mapped/conditional types |
| Declaration merging is needed | Tuple types or primitive aliases |
For JSON API responses, interface is the conventional choice. Either works.
Using Your Interface with fetch()
Once you have the interface, use it to type your API calls:
interface User {
id: number;
name: string;
email: string;
}
async function getUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error('Failed to fetch user');
const data: User = await response.json();
return data;
}
// TypeScript now knows data.name, data.email, data.id
const user = await getUser(1);
console.log(user.name); // β
Type-safe
Advanced: Generic API Response Wrapper
Many APIs wrap responses in a common envelope. Use generics to type it once:
interface ApiResponse<T> {
data: T;
status: number;
message: string;
pagination?: {
page: number;
total: number;
perPage: number;
};
}
// Usage:
type UserResponse = ApiResponse<User>;
type UserListResponse = ApiResponse<User[]>;
const response: UserResponse = await fetchUser(1);
TypeScript Utility Types for JSON Data
TypeScript's built-in utility types are extremely useful when working with JSON interfaces:
Partial<User>β makes all fields optional (useful for PATCH requests)Required<User>β makes all fields requiredReadonly<User>β prevents mutation of the objectPick<User, 'id' | 'name'>β select only specific fieldsOmit<User, 'password'>β exclude sensitive fieldsRecord<string, User>β dictionary/map of users
Best Practices
- Generate, then review β Use the tool to generate the base interface, then manually refine nullable and optional fields
- Keep interfaces close to their usage β Co-locate interface definitions with the files that use them
- Use
unknownoveranyβ For truly dynamic fields, preferunknownwhich forces you to narrow the type before use - Version your API types β When an API changes, create a new version of the interface rather than mutating the old one
- Share types between frontend and backend β Put shared interfaces in a
types/package that both sides import
Generate TypeScript interfaces instantly
Free, private, works with any JSON. No signup required.
Open JSON to TypeScript Tool βFrequently Asked Questions
user object has an address sub-object, the generator creates an Address interface and references it in the User interface as address: Address.interface is the conventional choice for describing plain API response objects. Use type when you need union types, intersection types, or other advanced type constructs.name?: string. Fields that are sometimes null can be typed as name: string | null. Use Partial<T> to make all fields optional at once, which is useful for PATCH request bodies.null means a value is explicitly absent. undefined means a variable has been declared but not assigned a value. In JSON, null is a valid value, but undefined is not β JSON.stringify strips undefined properties. Use field: string | null for nullable JSON fields and field?: string for fields that may not exist on the object.z.object(). Alternatively, use our JSON to Zod tool to generate a Zod schema directly from your JSON, then extract the TypeScript type with z.infer<typeof schema>.Related Tools & Guides
JSON to Python | JSON to Go | JSON to Java | JSON to Rust | JSON Schema Tutorial | How to Validate JSON | JSONPath Tutorial