AWS cron generator
Cron generator
Text counter
JSON to CSV converter
CSV to JSON converter
Unix timestamp converter
UUID v4 and v7 generator
JSON to TypeScript converter
Markdown to PDF
Base64
Images
JSON
QR Code
Passwords
Units
Hash
Colors
PDF Tools
PDF Editor
URL Encoder
Case Converter
Lorem Ipsum
Regex Tester
JWT Decoder
Text Diff
SVG Optimizer
EXIF Viewer
Color Extractor
Favicon Generator
Universal Converter
Hours Converter
PDF Splitter
Images to PDF
PDF to Image
Background Remover
Back to BlogUTILX / Notes & guides

JSON to TypeScript: what you can infer from an API response

JSON samples becoming TypeScript declarations

A JSON sample can reveal the shapes and primitive values that happened to appear in one response. It cannot prove the complete contract of an API. Turning that sample into TypeScript is still useful: it replaces repetitive transcription with a compilable starting point and makes uncertainty visible through unions, optional properties, and unknown[].

This guide feeds two synthetic records into the JSON to TypeScript tool. The sample includes a nullable property, a property missing from one record, a nested object with an optional member, numbers, and an empty array. The result is then treated as a draft to compare against documentation and runtime validation.

State what the sample can and cannot establish

If two array elements contain the same property with different observed types, the generator can form a union. If a property is absent from one object, it can mark that property optional. If the JSON explicitly contains null, it can include null. These conclusions are grounded in the supplied values.

Absence from a small sample is not proof that a property never exists. Presence in every sampled record is not proof that it is always required. An empty array contains no element from which to infer a type, so unknown[] is more honest than any[] or an invented domain type. Generated declarations help compile code; they do not inspect future network responses at runtime.

A sample designed to expose uncertainty

Use this exact JSON:

[
  {
    "id": 1,
    "tag": "alpha",
    "active": true,
    "notes": [],
    "meta": { "source": "api" }
  },
  {
    "id": 2,
    "tag": null,
    "notes": [],
    "meta": { "source": "cache", "age": 3 }
  }
]

Choose root name ApiResponse and type. The root describes an array of merged object elements. id is number; tag is a union of string and null; active is optional because the second object omits it; notes is unknown[] because both arrays are empty; and nested meta.age is optional. The generator emits deterministic, compilable TypeScript, although whitespace and inline-object layout are formatting details rather than part of the semantic test.

Generate, compile, and challenge the declaration

Open the localized tool, paste the fixture, enter ApiResponse, choose type, and generate. Copy the output into a .ts file in a project with strict checking enabled. Add a value matching each observed record and run the TypeScript compiler. The declarations should compile without diagnostics.

Now add a deliberate mismatch, such as a string id. Compilation should reject that assignment. This proves that the generated declaration participates in static checking; it does not prove an HTTP response will be checked. To test runtime data, parse a response and pass it through a schema validator or explicit guard before treating it as ApiResponse.

Repeat with interface. Object shapes can be emitted as interfaces, while a root array or primitive still requires a type alias for the root because an interface declares an object shape rather than aliasing an arbitrary type. Compare semantics, not whether every line starts with the selected keyword. Download the .ts output and compile that exact file before integrating it.

How the inference merges evidence

The generator walks every object in an array instead of inspecting only the first. It merges property sets, marks absent properties with ?, and combines observed value kinds in a stable union. Nested objects are represented as nested object types. Arrays inspect all available elements; heterogeneous elements become a union. Empty arrays have no element evidence and become unknown[].

JSON numbers become TypeScript number; the sample cannot establish integer versus floating-point domain rules. Strings remain string; the generator does not infer dates, URL brands, enums, or string literal unions from a handful of values. Explicit JSON null remains null, distinct from an absent property. Under strict null checking, callers must handle that union.

Property names that are not valid identifiers are quoted so the output remains legal TypeScript. The root name must itself be a valid TypeScript identifier. Stable ordering makes repeated generation reviewable: a change in output should reflect a change in the sample rather than arbitrary traversal.

Resolve input and generation errors

An invalid-JSON error should be fixed at the source. Look for trailing commas, unquoted keys, mismatched brackets, or a copied log prefix. The JSON errors guide provides a focused syntax workflow. Do not transform JavaScript object literal syntax into a type until you have valid JSON.

An invalid root name means the name contains punctuation, spaces, starts with a digit, or otherwise cannot serve as a TypeScript identifier. Use a descriptive name such as ApiResponse, not a filename or URL. Keys inside the JSON need not follow this rule because the generator can quote them.

Inputs larger than 200 KiB UTF-8 or deeper than 50 levels are rejected. Reduce the fixture to representative, synthetic records rather than deleting arbitrary closing structures. A deeply recursive production payload needs a designed model, not a mechanically expanded inline type.

Turn inferred output into a maintained contract

Compare the draft with the API’s authoritative schema and several representative responses: success, empty result, partial permissions, legacy records, and error cases. Change optionality and unions based on documented behavior, not personal preference. Name reusable nested concepts when that improves readability, and add comments that point to the contract version rather than the sample date alone.

Pair static types with runtime validation at the trust boundary. TypeScript types are erased when JavaScript runs. A type assertion can silence the compiler without checking a single byte. Parse JSON, validate unknown input, and only then expose the checked value to typed application code.

Avoid pasting real customer responses into examples. The converter works in the browser, but a copied fixture, downloaded .ts file, source-control commit, or screenshot can still disclose data. Minimize the sample and replace identifiers and free text with synthetic equivalents. The private developer tools guide covers the wider handling process.

Limits of sample-based types

The tool infers only from provided JSON. It cannot discover undocumented variants, conditional requirements, numeric ranges, semantic formats, discriminators that never vary, or future API changes. It does not make network requests, read OpenAPI, validate runtime values, generate codecs, or guarantee that the selected sample is representative.

All JSON numbers become number; strings do not become dates or enums; empty arrays become unknown[]; heterogeneous arrays use unions; and missing properties become optional only when the supplied object set demonstrates absence. The input limit is 200 KiB UTF-8 with maximum depth 50. Treat the output as a reviewable draft, not a substitute for an API contract.

Final TypeScript checklist

Use valid, synthetic JSON and a valid root identifier. Include more than one representative object when optionality or unions matter. Inspect every ?, null, union, nested object, and unknown[]. Choose interface or type according to the codebase while accepting a type alias when the root is not an object.

Compile the exact downloaded result under the project’s strict settings. Add one valid assignment and one deliberate invalid assignment. Compare with official API documentation, add runtime validation for untrusted responses, and record the sample cases used. Re-run inference when the contract changes, then review the diff rather than replacing maintained types blindly.

Keep the synthetic fixture beside that review so the inference remains reproducible.

Record its scope explicitly.

Sources: TypeScript Handbook: Object Types, TypeScript Handbook: Unions and Intersection Types, and RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format.