JSON Schema to TypeScript: Complete Code Generation Guide
When your API has 40+ endpoints, each with its own request and response schema, writing TypeScript interfaces by hand is not just tedious — it is a source of bugs. One missed field, one wrong type, and your compile-time safety is gone.
I automated this with JSON Schema to TypeScript generation. Here is exactly how it works and the edge cases that still require manual intervention.
The Basic Mapping
{
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0 },
"isActive": { "type": "boolean" },
"tags": { "type": "array", "items": { "type": "string" } },
"metadata": { "type": "object" }
},
"required": ["id", "name", "email"]
}
Generates:
interface User {
id: number;
name: string;
email: string;
age?: number; // Optional because not in "required"
isActive?: boolean;
tags?: string[];
metadata?: Record<string, unknown>;
}
The mapping rules:
| JSON Schema Type | TypeScript Type |
|---|---|
string | string |
integer | number |
number | number |
boolean | boolean |
null | null |
array | Array<T> |
object (with properties) | Interface |
object (no properties) | Record<string, unknown> |
Handling $ref References
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"user": { "$ref": "#/$defs/User" },
"address": { "$ref": "#/$defs/Address" }
},
"$defs": {
"User": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
}
},
"Address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
}
}
}
}
Generates:
interface User {
name: string;
email: string;
}
interface Address {
street: string;
city: string;
}
interface RootObject {
user: User;
address: Address;
}
On Stack Overflow, a question about handling $ref in code generation has 18,000 views. The tricky part is resolving circular references, which not all generators handle.
Enum and Union Types
{
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["active", "inactive", "suspended"]
},
"role": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
}
}
}
Generates:
type Status = "active" | "inactive" | "suspended";
interface User {
status: Status;
role: string | null;
}
The Edge Case That Broke Our Client
When a schema uses additionalProperties with a typed value:
{
"type": "object",
"properties": {
"metadata": {
"type": "object",
"additionalProperties": { "type": "string" }
}
}
}
Most generators output { [key: string]: string } or Record<string, string>. But our schema had:
{
"additionalProperties": {
"anyOf": [
{ "type": "string" },
{ "type": "integer" },
{ "type": "boolean" }
]
}
}
The generated Record<string, string | number | boolean> clashed with how our API actually sent the data (nested objects for some values). The fix was to add unevaluatedProperties: false to the schema and explicitly define every property.
Tools for Generation
| Tool | Approach | Best For |
|---|---|---|
json-schema-to-ts (npm) | Type-level (no code gen) | Direct schema import |
quicktype | Full code generation | Multiple languages |
openapi-typescript | OpenAPI → TypeScript | Full API clients |
json-schema-codegen | Template-based | Custom output formats |
Related Searches
- json schema to typescript
- generate typescript from json schema
- json schema typescript codegen
- json schema to typescript interface
- json schema ref typescript
- json schema enum typescript
- json schema additionalproperties typescript
- json schema code generation comparison
- openapi typescript generator
- json schema to typescript quicktype
Frequently Asked Questions
Can I generate TypeScript interfaces directly from JSON Schema?
Yes. Tools like json-schema-to-ts, quicktype, and openapi-typescript generate TypeScript interfaces from JSON Schema or OpenAPI specs. The generated types provide compile-time validation for API responses and request bodies.
How does code generation handle $ref and circular references?
$ref references are converted to TypeScript interface references. Circular references (where Schema A references Schema B which references Schema A) are handled by generating separate interfaces and using optional types or type aliases to break the cycle.
What about nullable properties?
JSON Schema represents nullable as { "anyOf": [{ "type": "string" }, { "type": "null" }] }. This generates as string | null in TypeScript. Properties not in the schema's required array generate as optional (?).
Should I check generated types into version control?
Yes. Generated types should be committed alongside your schema files. Include the generation command in your build script so types are regenerated when the schema changes. This ensures type safety is always in sync.
Final Thoughts
JSON Schema to TypeScript generation is one of the highest-ROI automation investments you can make for a TypeScript API client. It eliminates a class of bugs (field name typos, wrong types, missing fields) that are otherwise undetectable until runtime.