DevFlow logoDevFlow
Web Code
~5 min read
All Guides

Generating TypeScript Types from JSON: Schemas, Interfaces & Zod

How to generate type-safe TypeScript interfaces, Zod runtime validation schemas, and JSON Schema definitions from arbitrary JSON payloads.

Primary Interactive Tool:/tools/json-to-typescript

Consuming untyped JSON from third-party REST APIs, microservices, or webhook payloads is the leading cause of runtime TypeError: Cannot read properties of undefined in frontend applications and Node.js backends.

While TypeScript provides excellent compile-time developer ergonomics, types disappear completely once code compiles to JavaScript. To build resilient applications, you need a strategy that bridges static compile-time contracts with runtime boundary validation.

This guide demonstrates how to automatically transform raw JSON payloads into clean TypeScript interfaces, production-ready Zod schemas, and multi-language data models.


1. The Gap Between Static Types and Runtime Reality

In TypeScript, interfaces and type aliases are strictly design-time constructs. When you assert a type on an API response using the as keyword, you bypass the compiler without any actual runtime verification:

// ❌ Unsafe Type Assertion:
interface GitHubRepo {
  id: number;
  name: string;
  stargazers_count: number;
}

const response = await fetch('https://api.github.com/repos/wtool-dev/wtool');
const data = (await response.json()) as GitHubRepo;

// If GitHub changed the field to 'stargazersCount' or returned an error object,
// this line crashes at runtime despite zero TypeScript compiler errors:
console.log(data.stargazers_count.toLocaleString());

To guarantee absolute type safety, production applications implement a two-layer validation strategy:

  1. Static Type Generation: For rich IDE autocomplete, refactoring confidence, and compile-time type checking.
  2. Runtime Schema Parsing: To validate and sanitize external payloads the moment they cross the network boundary.

2. Generating Clean TypeScript Interfaces from JSON

When integrating with large, deeply nested REST APIs, hand-crafting TypeScript interfaces is time-consuming and prone to typos.

Using the DevFlow JSON to TypeScript Converter, you can paste any raw API payload or sample response to instantly infer a complete interface hierarchy:

export interface ApiResponse {
  status: 'success' | 'error';
  data: UserProfile;
  meta: ResponseMeta;
}

export interface UserProfile {
  id: string;
  email: string;
  roles: string[];
  settings: UserSettings;
  lastLoginAt: string | null;
}

export interface UserSettings {
  theme: 'dark' | 'light' | 'system';
  twoFactorEnabled: boolean;
  emailNotifications: boolean;
}

export interface ResponseMeta {
  requestId: string;
  executionTimeMs: number;
}

Need schema validation for JSON Schema Draft 2020-12? Use our JSON to Schema Converter or read our JSON Formatting & Validation Best Practices Guide.


3. Generating Zod Schemas for Runtime Type Safety

Zod has become the gold standard for TypeScript schema validation. Defining your data contracts in Zod gives you runtime validation and automatic static type inference from a single source of truth:

import { z } from 'zod';

// 1. Define runtime validation schema
export const UserProfileSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  roles: z.array(z.string()).nonempty(),
  settings: z.object({
    theme: z.enum(['dark', 'light', 'system']),
    twoFactorEnabled: z.boolean(),
    emailNotifications: z.boolean(),
  }),
  lastLoginAt: z.string().datetime().nullable(),
});

// 2. Infer static TypeScript type directly from schema (Zero duplicate code)
export type UserProfile = z.infer<typeof UserProfileSchema>;

// 3. Parse and validate network payloads safely
export async function fetchUserProfile(userId: string): Promise<UserProfile> {
  const res = await fetch(`/api/users/${userId}`);
  const rawData: unknown = await res.json();

  const parseResult = UserProfileSchema.safeParse(rawData);
  if (!parseResult.success) {
    console.error('API Schema Violation:', parseResult.error.flatten());
    throw new Error('Received malformed user payload from server.');
  }

  // Strongly typed and validated at runtime:
  return parseResult.data;
}

Generate Zod schemas automatically from raw JSON with our JSON to Zod Converter.


4. Advanced Patterns for API Boundaries

Handling Optional vs Nullable Fields

In JSON, an omitted property ({}) differs from an explicitly null property ({"value": null}). Represent this precisely in Zod and TypeScript:

  • Optional field (may be omitted): z.string().optional()string | undefined
  • Nullable field (explicitly null): z.string().nullable()string | null
  • Both: z.string().nullish()string | null | undefined

Discriminated Unions for Polymorphic Payloads

For event streams or webhook payloads containing varying structures based on a type tag, use discriminated unions:

const WebhookEventSchema = z.discriminatedUnion('type', [
  z.object({
    type: z.literal('payment.succeeded'),
    amount: z.number().positive(),
    currency: z.string().length(3),
  }),
  z.object({
    type: z.literal('user.signup'),
    userId: z.string().uuid(),
    source: z.string(),
  }),
]);

export type WebhookEvent = z.infer<typeof WebhookEventSchema>;

Multi-Language & Specification Type Generation

If your API publishes formal Swagger or OpenAPI schemas, or your infrastructure spans multiple programming languages, generate strongly typed data models across your entire stack:


Frequently Asked Questions

What is the difference between static TypeScript interfaces and Zod schemas?

TypeScript interfaces exist only during development and are completely erased during compilation to JavaScript, providing zero protection against invalid API data at runtime. Zod schemas execute at runtime in JavaScript to inspect and validate data shapes while automatically generating matching TypeScript types via z.infer.

How do I handle optional vs nullable JSON fields when generating types?

In JSON data contracts, optional fields are properties that can be missing from an object, while nullable fields are properties explicitly present with a null value. In TypeScript, declare optional fields with field?: string and nullable fields with field: string | null. In Zod, use .optional() and .nullable() respectively.

Can I generate TypeScript types from large, complex JSON files automatically?

Yes. Paste your raw JSON into our JSON to TypeScript Converter or JSON to Zod Converter. The tools analyze property types, recursively infer sub-interfaces, and resolve type unions in your browser.

Interactive Tools for this Guide

Free, browser-based utilities to test, validate, and inspect workflows related to “Generating TypeScript Types from JSON: Schemas, Interfaces & Zod”.

100% Client-Side • No Setup