JSON Toolkit

Generating TypeScript types from a Schema

Bottom line: json-schema-to-typescript and quicktype emit a .d.ts in one command, but it is a draft: optionality, unions, additionalProperties and dates need a human pass.

Intermediate~4 min readPublished 2026-10-30Updated 2026-10-30
Generating TypeScript types from JSON Schema

Bottom line: generate with json-schema-to-typescript or quicktype, then hand-fix optionality, unions, additionalProperties and dates; keep Schema as the source and types as output, enforced by a types:check step in CI.

One command gets you a file. It does not get you correct types.

The one-liner

npx json-schema-to-typescript schema.json > src/types/order.d.ts

It handles $ref, allOf, oneOf, anyOf, and converts description into JSDoc. For batch generation with a banner and a chosen style, call its Node API from a small build-types.mjs.

quicktype is the alternative, and can also infer a schema from sample JSON:

npx quicktype -s schema schema.json -o order.ts --lang typescript

Four things to fix afterwards

Optionality. Anything not in required becomes ?. But "always present, may be null" and "may be absent" are different contracts:

interface Order {
  remark?: string | null;   // may be missing
  status: string;
}

If the backend always sends remark: null, drop the ? and stop making every consumer guard for undefined.

Undiscriminated unions. oneOf usually becomes A | B, which TypeScript cannot narrow without a literal field. Add a fixed type property to each branch in the Schema.

additionalProperties. Omitted or true sometimes yields [k: string]: unknown, sometimes any. The first is safe, the second silently disables type checking for the whole object.

Date formats. format: "date-time" is a string on the wire. Mapping it to Date means you must also deserialize, or the runtime value stays a string while the type lies.

Keep the direction fixed

Schema is the source, types are output. Generating the Schema from hand-written types is acceptable only if regeneration is mandatory and enforced in CI — otherwise both sides drift and nobody knows which is authoritative.

Put it in the pipeline

"types:gen": "json2ts -i schemas/ -o src/types/generated",
"types:check": "pnpm types:gen && git diff --exit-code src/types/generated"

types:check is the part that matters: change the Schema without regenerating and CI fails.

FAQ

Why does the generated file contain any?

It usually comes from additionalProperties being true or omitted, which some generators map to [k: string]: any. Replace it with unknown or explicit fields, because any disables type checking for the whole object.

How should I fix optional fields after generation?

Anything outside required becomes ?, but "always present, may be null" differs from "may be absent". If the backend always sends the field, drop the ?.

Should CI fail when the Schema and types drift?

Yes. A types:check script that regenerates and runs git diff --exit-code fails the build as soon as the Schema changes without regeneration.

Further reading