Skip to tool
ecech.
💻 Developer & Code

JSON to TypeScript Interfaces That Tell Optional Apart From Nullable

A missing key and a null key are different types. Most generators emit any for both. This merges every array element first, then explains each decision.

TypeScript



Decisions worth checking

Advertisement

How the calculation works

Two elements of one array. Look at what differs. { id: 1, name: "Ada",   nickname: null } nickname is present, and null { id: 2, name: "Grace",   lastSeen: "2026-.." } nickname is absent entirely What most generators emit nickname: any lastSeen: string compiles, then throws on element 2 What is actually true nickname?: string | null lastSeen?: string optional and nullable are different facts

How to Use This Tool

Paste a response, get interfaces. Read the decisions panel underneath — it lists the places where one sample could not tell the whole truth.

Optional and nullable are not the same thing

This is the distinction that most generated types get wrong, and it matters because TypeScript enforces them differently:

  • nickname?: string — the key may be absent. Reading it gives undefined.
  • nickname: string | null — the key is always there and may hold null.
  • nickname?: string | null — both are possible.

Across an array, a key that appears in some elements and not others is optional. A key that always appears but is sometimes null is nullable. Those are different observations about your API and only merging every element can tell them apart. A generator reading element zero sees neither.

What null in a sample really means

A field whose only observed value is null carries almost no information. Its type is null, which is technically correct and useless — you know it will hold something else sometimes, you just have not seen it. Emitting any here, as many tools do, is worse: it switches off type checking for that field silently.

This tool types it null and tells you, in the decisions panel, that it needs a human. That is the honest answer: the sample does not contain the information, so the generator should say so rather than invent a type.

Arrays are the union of everything in them [1, 2, 3] number[] [1, "a"] (number | string)[] not number[] [] unknown[] the sample says nothing An empty array is the honest hard case. There is no element to learn from, so any type you emit for it is a guess. unknown[] forces you to look.
Reading only element zero turns a union into a lie that compiles.

The empty array problem

An empty array in your sample gives no information about its element type. There are three ways to handle it and only one is honest:

  • any[] — silently disables checking for everything read out of it.
  • never[] — technically what an empty literal is, and it will reject every push.
  • unknown[] — forces you to narrow before use, which is the correct response to not knowing.

This tool emits unknown[] and flags it. If you know what belongs in there, you are the only one who does.

Date strings stay strings

With the flag on, fields that look like ISO 8601 timestamps are noted in the decisions panel but still typed string. That is deliberate. JSON.parse returns a string; nothing turns it into a Date on its own. Typing it as Date would be a lie about what is in the variable at runtime, and it is a lie the compiler cannot catch. Convert explicitly at the boundary, and let the type describe reality.

What a sample cannot tell you

Everything here is inferred from one example, so treat the output as a strong first draft, not a contract. A sample cannot show you a field that was absent that day, an enum value that had not occurred yet, or a number that is sometimes a string because an upstream service stringifies large integers. If the API publishes an OpenAPI or JSON Schema document, generate from that instead — it states what is possible rather than what happened once.

Advertisement

Frequently Asked Questions

What is the difference between an optional and a nullable field in TypeScript?
An optional field (name?: string) may be absent from the object, so reading it can give undefined. A nullable field (name: string | null) is always present and may hold null. They are enforced differently, and only comparing every element of an array can tell you which one your API does.
Why do JSON to TypeScript tools produce any?
Usually because they read a single object and hit a value they cannot classify — most often null, or an empty array. Emitting any makes the output compile while silently switching off checking for that field, which is the least useful outcome. Reporting the uncertainty is more helpful than papering over it.
What type should an empty array get?
unknown[]. The sample contains no element to learn from, so any concrete type is a guess. any[] disables checking on everything read out of it, and never[] rejects every write. unknown[] forces a narrowing step at the point of use, which is the correct response to genuinely not knowing.
Why are date strings typed as string rather than Date?
Because JSON.parse returns a string and nothing converts it automatically. Typing it Date would describe something that is not in the variable at runtime, and the compiler cannot catch that mismatch. Convert explicitly where the data enters your code and let the type stay honest.
How is an array of differently shaped objects handled?
Every element is merged. Keys present in all of them are required, keys present in only some become optional, and conflicting value types become a union. Reading only the first element would turn a union into a type that compiles and then fails on the second element.
Is this good enough to use as an API contract?
No, and nothing inferred from a sample is. A sample cannot show a field that happened to be absent, an enum value that had not occurred yet, or a number that is sometimes stringified upstream. If the API publishes OpenAPI or JSON Schema, generate from that — it describes what is possible rather than what happened once.
Is my JSON uploaded anywhere?
No. It is parsed and inspected in your browser and never transmitted, which matters given that API samples routinely contain tokens and personal data.

Related tools in Developer & Code

Browse all Developer & Code tools