Why Your TypeScript Types Don't Match Runtime JSON (And How to Fix It)
Manual TypeScript interfaces drift from actual API responses. Learn how to generate accurate types from JSON, handle optional fields, and prevent runtime type errors.
You just integrated a third-party API. The documentation shows a sample response. You carefully type out the TypeScript interface:
typescriptinterface User {
id: number;
name: string;
email: string;
}Your code compiles. Tests pass. You deploy to production.
Three weeks later, a QA engineer files a bug: "App crashes when loading guest users." You check the logs and see the API returned email: null for unverified accounts.
Your carefully crafted types just lied to you in production.

The "Documentation vs. Reality" Gap
API documentation is often outdated or incomplete. Even when it's accurate, it shows the "happy path" — not the edge cases where fields become null, arrays are empty, or entire objects are missing.
The Three Sources of Type Drift
1. Optional Fields That Aren't Documented
The docs say email is always present. But in reality:
- Free tier users don't have emails verified →
email: null - Deleted accounts return partial data →
emailfield missing entirely - Legacy data from 2015 →
email: ""
2. Nested Complexity You Didn't Notice
You glance at the JSON and think "simple object". Then you paste it into your code and realize:
json{
"user": {
"profile": {
"settings": {
"notifications": {
"email": { "enabled": true, "frequency": "daily" }
}
}
}
}
}Typing this manually means writing 5 nested interfaces. Miss one level, and you get runtime crashes.
3. Array Type Ambiguity
json{
"tags": ["javascript", "typescript"],
"metadata": []
}Is metadata an array of strings? Objects? Mixed types? You won't know until you inspect actual production data — or until it crashes.
The Manual Typing Trap
Let's say you decide to be thorough. You copy the JSON response and start typing:
typescriptinterface ApiResponse {
id: number;
name: string;
// ... 50 more fields
}Problems with this approach:
- Time-consuming: A 200-line JSON response takes 15+ minutes to type correctly.
- Error-prone: You'll mistype field names (
userNamevsusername), guess wrong types (stringwhen it should bestring | null), or forget nested objects. - Not maintainable: When the API changes (and it will), you have to manually diff the old and new responses to update your types.
The "any" Escape Hatch (And Why It's Worse)
Frustrated developers often do this:
typescriptconst data: any = await fetch('/api/user').then(r => r.json());This "works" but defeats the entire purpose of TypeScript. You lose:
- Autocomplete in your IDE
- Compile-time error checking
- Refactoring safety
You're back to writing JavaScript with extra steps.
How Auto-Generation Solves This
Instead of guessing or giving up, generate types directly from actual JSON responses.
The Workflow
- Capture Real Data: Use your browser DevTools Network tab to copy the actual API response (not the docs example).
- Generate Types: Paste the JSON into a converter that analyzes the structure.
- Get Accurate Interfaces: Receive TypeScript code that matches reality.
Example Input (Real API Response):
json{
"id": 12345,
"username": "alice_dev",
"email": null,
"roles": ["admin", "editor"],
"metadata": {
"lastLogin": "2025-12-10T08:30:00Z",
"loginCount": 42
}
}Generated Output:
typescriptinterface RootObject {
id: number;
username: string;
email: null;
roles: string[];
metadata: Metadata;
}
interface Metadata {
lastLogin: string;
loginCount: number;
}Notice how it correctly detected:
email: null(notstring)roles: string[](array type)- Nested
Metadatainterface
Handling Edge Cases: The Smart Type Inference
Good generators go beyond basic type detection:
Union Types for Nullable Fields
If you provide multiple JSON samples where email is sometimes null and sometimes a string, a smart tool generates:
typescriptemail: string | null;This matches real-world APIs where fields are conditionally present.
Optional vs. Null vs. Undefined
TypeScript has three ways to express "might not be there":
typescriptinterface User {
email?: string; // Field might not exist
phone: string | null; // Field exists but value is null
bio: string | undefined; // Explicitly undefined
}The best approach depends on your API contract. Manual typing forces you to guess. Auto-generation from real responses gives you the truth.
Readonly Arrays and Tuples
Some APIs return fixed-length arrays (tuples):
json{
"coordinates": [40.7128, -74.0060]
}A naive converter gives you number[]. A better one detects the pattern and generates:
typescriptcoordinates: [number, number]; // Tuple: exactly 2 numbersThe Maintenance Problem
APIs evolve. A new field gets added. An old field becomes deprecated. If you manually maintain types, you need to:
- Notice the API changed (hope you have good monitoring)
- Find the exact difference
- Update your interface
- Update all consuming code
With auto-generation:
- Copy the new API response
- Re-generate types
- TypeScript compiler shows you exactly what broke
Your IDE highlights every place that needs updating. No guessing.
Security Consideration: Client-Side Generation
If you're working with internal APIs that return sensitive data (user emails, financial records), do not paste them into random online converters.
Many "free" tools upload your JSON to their servers for processing. You don't know if they log it, sell it, or leave it in an unsecured S3 bucket.
For sensitive work, prioritize local processing:
Verification Test
To check if a browser-based tool is truly client-side: disconnect your internet and try to convert. If it still works, it's processing locally. Tools like JSON to TypeScript pass this test.
Alternative: Command-Line Tools
For maximum security, use local CLI tools:
bash# Using quicktype (supports 20+ languages)
quicktype data.json -o types.ts
From URL (still downloads locally, then processes)
quicktype https://api.example.com/user -o User.ts
With options
quicktype data.json \
--lang typescript \
--out types.ts \
--just-types \
--nice-property-namesbash# Using json-schema-to-typescript (best for OpenAPI specs)
json2ts -i schema.json -o types.ts
Batch processing multiple files
for file in api-responses/*.json; do
quicktype "$file" -o "types/$(basename "$file" .json).ts"
doneChoosing the Right TypeScript Generator
When evaluating JSON to TypeScript tools, consider these factors:
Essential Features:
- Automatic Type Detection: Converts
19.99tonumber(notstring) - Nested Object Support: Generates separate interfaces for complex structures
- Union Type Inference: Detects
string | nullfrom multiple samples - Array/Tuple Recognition: Distinguishes
string[]from[string, number]
Security Features:
- Client-Side Processing: No server uploads (verify by disconnecting internet)
- No Data Retention: Files processed in memory only
- Open Source (Bonus): Allows security audits
Popular Options:
Browser-Based Tools:
- JSON to TypeScript - Client-side, instant conversion
- json2ts.com - Simple interface, good for quick tasks
- transform.tools - Multi-format support (JSON/GraphQL/Flow)
Command-Line Tools:
- quicktype - Best for production pipelines, supports 20+ languages
- json-schema-to-typescript - Ideal for OpenAPI/JSON Schema specs
- dtslint - Type validation and testing
IDE Built-ins:
- VS Code: "Paste JSON as Code" extension by quicktype
- IntelliJ IDEA: Built-in "Paste as TypeScript" feature
- WebStorm: "Paste JSON as TypeScript interface" action
Best Practices Checklist
Before generating types from JSON:
- Use Real Data: Don't rely on documentation examples. Copy actual API responses from your Network tab.
- Test Edge Cases: If possible, capture responses for error states, empty arrays, and null fields.
- Validate with Multiple Samples: If you have 3 different user objects, generate types from all of them to catch optional fields.
- Add Strict Null Checks: Enable
strictNullChecksin yourtsconfig.jsonto catchnull/undefinedissues at compile time. - Review Generated Code: Auto-generation is 95% accurate, but always review for business logic (e.g., should
pricebenumberor a customMoneytype?).
When to Regenerate Types
Regenerate your types when:
- The API version changes (v1 → v2)
- You notice runtime errors related to unexpected field types
- New fields appear in responses
- You're integrating a new endpoint
Don't regenerate for every single API call — that's overkill. But do it whenever the contract changes.
Automated Type Generation in CI/CD
For production systems, integrate type generation into your build pipeline:
bash# In package.json scripts
{
"scripts": {
"generate:types": "quicktype api-responses/*.json -o src/types/",
"prebuild": "npm run generate:types",
"test:types": "tsc --noEmit"
}
}yaml# GitHub Actions example
- name: Generate API Types
run: |
curl https://api.example.com/schema > schema.json
quicktype schema.json -o src/types/API.ts
- name: Verify Types Compile
run: npm run test:typesQuick Decision Guide
For one-time API integrations (<100 fields):
- Use browser-based tools like JSON to TypeScript for quick conversions
- Verify client-side processing by disconnecting internet
For recurring tasks or large schemas:
- Script with quicktype or json-schema-to-typescript
- Add to CI/CD pipeline for automatic updates
For sensitive/regulated data:
- Use command-line tools on local machines
- Never upload customer data to public converters
For team workflows:
- Commit generated types to version control
- Use OpenAPI specs as single source of truth
- Automate generation on API changes via webhooks
Summary
Manual TypeScript interfaces are:
- Slow to write
- Easy to get wrong
- Hard to maintain
Auto-generated types from real JSON are:
- Instant
- Accurate to actual responses
- Easy to update when APIs change
Next time you integrate an API:
- Copy the real JSON response (not docs)
- Generate TypeScript interfaces
- Enable strict null checks
- Let the compiler catch mismatches
Your QA team will thank you when they can't find bugs to file.
Ready to Generate Your Types?
For quick one-time conversions, try browser-based tools with client-side processing. For recurring tasks or CI/CD integration, use command-line tools like quicktype. Always verify tools don't upload sensitive API responses.