Stockio

Generate TypeScript Types From JSON and API Data With AI

Generate TypeScript contracts from representative API responses, model nullable fields and variants accurately, and add runtime checks before data reaches your interface.

By Ravi Desai

DevOps & Security Engineer ·

On this page

An API response is evidence of what happened once, not a complete description of what can happen. Give an AI assistant one successful response and it will often produce a tidy interface that assumes every field is present, every collection has the same shape and every status is already known.

Useful generated types need a broader foundation: representative examples, the provider's contract and explicit decisions about uncertainty. The goal is to make invalid assumptions visible at integration boundaries, not to persuade the compiler that untrusted network data is safe.

Collect evidence across response states

Start with representative payloads rather than the prettiest example in the documentation. Collect empty results, populated results, pagination boundaries, optional relationships and documented error responses. Include examples from relevant permission levels because restricted users may receive omitted fields that administrators always see.

Sanitise fixtures before sharing them with an assistant. Replace personal information and credentials while preserving structural distinctions, such as a missing property versus a property containing null. Avoid replacing every value with the same string if that obscures which fields are dates, identifiers or free text.

Compare observations against the documented contract. A field present in every sample may still be optional; a status absent from the sample set may still be valid. If an OpenAPI description exists, inspect its schema rather than treating inference from examples as the primary authority. The OpenAPI specification defines how operations and response schemas can be described.

Record disagreements for the provider or integration owner. Do not silently resolve a nullable field in favour of whichever interpretation makes the UI easiest to write. A short uncertainty log gives reviewers a concrete list of assumptions to approve or investigate.

Constrain the model's inference

Ask the assistant to produce both a type draft and an explanation of its evidence. Require it to distinguish observed shape from guaranteed schema. Include naming conventions, identifier rules and the runtime validation library already used in the repository, if any.

A useful prompt sets limits explicitly:

Generate TypeScript DTO types from these sanitised responses
and the attached API schema. Preserve string identifiers.
Explain absent versus null fields. Do not infer enum members
from examples alone. List contradictions and unknowns.
Keep transport types separate from UI view models.

Prefer unknown over invented certainty when data is genuinely unspecified. Do not ask for all fields to become optional merely to eliminate compiler errors. That loses information and pushes ambiguity into every consumer. Instead identify the specific fields whose presence varies and explain why.

Use the AI coding assistants directory to compare tools that can read local schemas and existing adapters. Repository context helps the model follow conventions, but does not give it knowledge of undocumented server behaviour. The wider AI hub is useful for planning where generated code still needs human review.

Model presence, variants and identifiers precisely

The distinction between absence and null matters in API clients. An omitted property may mean “not requested”, while null may mean “known to have no value”. Model those meanings according to the contract rather than collapsing them for convenience. Compiler options can also affect how optional properties behave during assignment.

Keep identifiers as strings when the service defines them that way. Numeric-looking identifiers may have leading zeros or exceed safe integer precision. Similarly, JSON date strings remain strings until your code explicitly parses them. A type alias named DateString can document intent without performing runtime validation.

Use discriminated unions when a stable discriminator distinguishes variants. For example, a successful result might have a value while a rejected result has an error code. Do not mark both fields optional on one broad object if the API guarantees mutually exclusive states.

type ImportResult =
| { kind: "accepted"; jobId: string }
| { kind: "rejected"; reason: string };

function describe(result: ImportResult): string {
if (result.kind === "accepted") return result.jobId;
return result.reason;
}

Check the TypeScript narrowing documentation for the semantics behind this pattern. The union is appropriate only if those variants reflect a real contract. Do not manufacture a discriminator that the server never sends without implementing an explicit adapter.

Validate data at runtime

A type annotation does not inspect a response body. Treat decoded JSON as untrusted input until it passes the checks your application requires. Casting it to an interface makes a compiler assertion, not a validation step, and can move failures far away from the original request.

Choose a runtime schema library already supported by the project, or a focused manual validator for a small shape. Zod's documentation illustrates the distinction between schema definitions and parsed output. Where possible, derive static types from reviewed runtime schemas to avoid maintaining two drifting definitions.

Decide how to handle unknown fields and variants. An additive field from the server should not necessarily break an older client. An unknown payment state, however, must not silently become success. Define whether to reject, preserve or ignore unfamiliar data according to the consequence of getting it wrong.

Validate at the boundary once, then pass the checked value inward. Capture useful failure context without logging entire sensitive payloads. A validation error should identify the route, schema version when available and failing field path, allowing engineers to diagnose drift without exposing user records.

Separate transport data from interface needs

Keep DTOs and view models distinct. The API might expose nullable names, raw timestamps and internal status codes; the interface may need a display label, a formatted date and a specific empty state. Put that transformation in a named adapter instead of altering the transport type to match one component.

For a status badge built from the resulting view model, use the CSS to Tailwind tool to translate its existing CSS into utility classes after deciding the supported states. This is a presentation aid, not a type generator: verify that accepted, pending, rejected and unknown states retain distinct text and accessible styling. Keeping that styling task separate prevents UI preferences from distorting the API contract.

Add contract regression fixtures covering each important variant and nullable path. Verify that malformed examples fail at the boundary and supported examples reach the intended UI state. The article on AI-generated unit tests explains how to make expected outcomes independent of generated implementation details.

When the API changes, review schema differences, refresh representative fixtures and regenerate only the affected draft types. Inspect the diff before adopting it. A successful compile is one useful signal, but runtime fixtures and adapter tests establish whether the new contract still supports the product's actual journeys.

Key takeaways

  • Combine representative responses with the provider's documented schema.
  • Preserve meaningful distinctions between omitted, null and variant fields.
  • Keep string identifiers and raw date representations faithful to transport data.
  • Validate network responses at runtime before trusting static types.
  • Separate API contracts, UI adapters and presentation changes during review.
Featured sponsor

Reach designers & developers at work

Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.

Become a sponsor

Keep reading

Related articles

More in Dev Tools & AI Coding
Dev Tools & AI Coding

Write Documentation and READMEs With AI

AI is useful for developer documentation when it reorganises evidence you already trust. It is unreliable when asked to invent a setup guide from a project name and a directory listing. The difference is not.

Includes: CSS to TailwindRavi DesaiRavi Desai
Dev Tools & AI Coding

MCP Server Explained: Model Context Protocol for Web Devs

Understand MCP hosts, clients, resources, and tools, then build a narrow web-development integration with explicit permissions, injection defences, and useful operational tests.

Includes: CSS to TailwindSofia MarchettiSofia Marchetti
Dev Tools & AI Coding

Prompts for Coding: How to Brief an AI Coding Assistant

Write coding prompts that define observable behaviour, architectural limits, and useful examples, then turn uncertain requirements into reviewable implementation steps.

Includes: CSS to TailwindGrace ChenGrace Chen
Design Systems

10 Best Tailwind CSS Component Libraries

Find a Tailwind component library for landing pages, dashboards, or custom products, with ten options explained through their real strengths, trade-offs, and ideal use cases.

Includes: CSS to TailwindMaya LindqvistMaya Lindqvist
Design Systems

10 Best React UI Component Libraries

Compare ten established React component libraries for dashboards, accessible forms, and polished apps, with practical strengths, limitations, and advice for choosing a sensible fit.

Includes: CSS to TailwindJonah WeissJonah Weiss
HTML & CSS

10 Best CSS Frameworks

Compare ten popular CSS frameworks, from utility classes to ready-made components, and find an approach that fits your team's skills, design needs, and project size.

Includes: CSS to TailwindMarcus BellMarcus Bell