json-schema-to-typescript: compiling JSON Schema into TypeScript declarations
Compile JSON Schema to TypeScript type declarations
At a glance
- What is it?
- A CLI and Node library that turns JSON Schema documents into .d.ts interfaces, including a cross-file imports mode. It is a build-time code generator, not a runtime validator.
- Who is it for?
- Adopt it if your JSON Schema files are the source of truth for an API or config format and you want the TypeScript side generated rather than hand-written, and if you can run Node.js 22.19 or later in the build. Do not adopt it if you need runtime validation: this package emits declarations only, and the README points to no validation step.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 23 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What json-schema-to-typescript replaces
If a service publishes a JSON Schema and a TypeScript client consumes the same payloads, someone normally maintains two descriptions of one shape. The schema drifts, or the interface does. This package removes the second description by compiling the first into TypeScript declarations: the README's example takes an object schema with firstName, lastName, age and hairColor and emits an exported interface named after the schema title, with age optional and hairColor narrowed to the union "black" | "brown" | "blue". Field descriptions become JSDoc comments above the property.
The audience is narrow and specific. It fits teams that already treat JSON Schema as the contract for an HTTP API, a config file or a message format, and that want the TypeScript side produced by a build step rather than typed by hand. It does not fit teams whose source of truth is a TypeScript type they want to export as JSON Schema; that is the opposite direction, and the repository name says which way it runs.
The compiler pipeline and what it does to $ref
The published entry point is dist/src/index.js with typings at dist/src/index.d.ts, and the package ships one binary, json2ts, mapped to dist/src/cli.js. Input can arrive as a file path, a directory, a glob, or piped stdin, and the CLI writes to a file or to stdout. Internally the repository depends on @apidevtools/json-schema-ref-parser, which is what resolves $ref pointers before the schema is walked and rendered as TypeScript source.
The default behaviour of that resolution matters more than it first appears. Each input file is compiled on its own, so a type that several files reach through a $ref is declared again in every output file. The README is explicit about the consequence: without --imports, a.d.ts and b.d.ts each declare their own Thing, Tag, A and B, plus a second A1/B1 where the cycle comes back around. Duplicated declarations are harmless for type checking in most setups, but they are not free: two structurally identical interfaces from two files are different nominal declarations in the eyes of anything that compares types by identity, and readers of the generated code have to work out which copy is authoritative.
The --imports flag, described as experimental, changes that. A directory or glob is compiled as one set, and a type living in another file of the set becomes an import type from that file's module instead of a copy. Import paths are relative and end in .js, which the README says TypeScript resolves to the .d.ts or .ts next to it under every moduleResolution setting. Naming collisions are handled by renaming on import, for example import type {Thing as Thing1}, and each file keeps the type names it would have had on its own. Every file's definitions are declared whether or not the file uses them, so --unreachableDefinitions is effectively on for the set. Two constraints come with it: --cwd cannot be combined with --imports, and --imports needs an output directory.
Installing json-schema-to-typescript and generating a first .d.ts
The package is on npm and the README gives a single install command. Node.js 22.19 or later is required; the README notes that the 16.x line is the last release line that runs on Node.js 16 through 20, so an older runtime pins you to an older major.
npm install json-schema-to-typescriptA local install puts the json2ts binary in node_modules/.bin, reachable through npx. The README also documents a global install, which exposes json2ts directly, and an npx form that pulls the package into the npm cache without a separate install step:
# install locally, then use `npx json2ts`
npm install json-schema-to-typescript
# or install globally, then use `json2ts`
npm install json-schema-to-typescript --global
# or install to npm cache, then use `npx --package=json-schema-to-typescript json2ts`
# (you don't need to run an install command first)With the binary available, the shortest path is a single input file to a single output file. The example directory in the repository contains person.json and person.d.ts, so that pair is the shape to compare against:
cat foo.json | json2ts > foo.d.ts
# or
json2ts foo.json > foo.d.ts
# or
json2ts foo.yaml foo.d.ts
# or
json2ts --input foo.json --output foo.d.ts
# or
json2ts -i foo.json -o foo.d.tsFor a whole directory, quote the glob so the shell does not expand it, and pass an output directory:
json2ts -i 'schemas/**/*.json' -o types/Style options are passed as CLI flags, and boolean flags can be turned off with the no- prefix. The CLI also discovers the closest Prettier configuration for the output file, with explicit --style.* flags taking precedence, and the output is always parsed as TypeScript whatever parser the config names:
# generate code for definitions that aren't referenced
json2ts -i foo.json -o foo.d.ts --unreachableDefinitions
# use single quotes and disable trailing semicolons
json2ts -i foo.json -o foo.d.ts --style.singleQuote --no-style.semiA Prettier config that cannot be loaded now fails the run, which is worth knowing if your repository has a config with a missing plugin. The programmatic API is unaffected by that behaviour.
Where the generated types stop helping
The output is declarations. Nothing in the README describes a runtime check, so a payload that arrives over the network is typed but not verified: the generated interface tells the compiler what to expect, not what actually showed up. Teams that need both usually pair a generator like this with a validator, and the two must be kept in step, which reintroduces a smaller version of the duplication the generator was meant to remove.
The --imports mode is the other place to be careful. The README labels it experimental, and the rules for what is importable are narrower than the flag name suggests. A file can import its root type, everything under its definitions or $defs, and any other named schema its root type reaches. A $ref to anything else in the file, such as other.json#/properties/x with no title, is declared inline as before. So is a $ref to a file outside the set or to a URL, and so is a $ref that carries keywords of its own, such as a description, on the reasoning that it describes a different type. Schemas under other keys, OpenAPI's components/schemas among them, are not importable yet. If your schemas are OpenAPI fragments rather than standalone JSON Schema documents, expect the inline fallback rather than imports.
Alternatives and the direction of the arrow
The related searches around this project point at two different neighbours, and they are worth separating. Tools such as json-schema-to-zod move schema to a runtime validator: you get a Zod schema that both validates at runtime and can be inferred into a TypeScript type, so the schema and the check stay in one artefact. This package produces no validator, which is the trade: less runtime code, but also no runtime guarantee. If your problem is untrusted input, the Zod direction addresses more of it.
Tools such as ts-json-schema-generator run the opposite way, taking TypeScript types and emitting JSON Schema. That is the right choice when the TypeScript code is the source of truth and the schema is a derived artefact for documentation or for another language's client. Choosing between the two is really choosing which artefact a human edits. There is also a browser demo linked from the README, which is convenient for a one-off schema but not a substitute for a build step.
Maintenance, licence and the cost of upgrading
The repository is not archived, and the last push was on 2026-09-07. The licence is MIT, declared in package.json and in LICENCE.md, which permits commercial use and modification with the copyright notice retained; this is a description of the terms, not legal advice, and the full text is the authority.
The upgrade cost is concentrated in the runtime requirement. The current release line requires Node.js 22.19 or later, and the README states that 16.x is the last line supporting Node.js 16 through 20. A project on an older runtime therefore has a ceiling on which major it can take, and moving past that ceiling is a Node upgrade first and a package upgrade second. The package version in the repository is 16.0.0, so the Node 22.19 requirement is the direction the next major is heading rather than something already shipped to npm. The repository lists no release notes beyond the changelog file, so the changelog is where to look for behaviour changes between versions. Test scripts in package.json include a fuzz run, a corpus run and a conformance run, which suggests the maintainers treat schema edge cases as a first-class concern; that is a signal about process, not a guarantee about your particular schema.
Editorial conclusion
Adopt it if your JSON Schema files are the source of truth for an API or config format and you want the TypeScript side generated rather than hand-written, and if you can run Node.js 22.19 or later in the build. Do not adopt it if you need runtime validation: this package emits declarations only, and the README points to no validation step. Before wiring it into CI, run json2ts on one representative schema directory with --imports and read the generated .d.ts files, because the cross-file mode is described as experimental and the naming and import behaviour it produces is what your downstream code will have to compile against.
Frequently asked questions
Is JSON Schema a thing?
Yes. json-schema-to-typescript takes JSON Schema documents as input and compiles them into TypeScript declarations, and the README's example shows an object schema with properties, an enum, a minimum and a required list producing an exported interface.
How can I read a JSON file in TypeScript?
This package does not read JSON data at runtime. It reads a JSON Schema file and writes a .d.ts declaration, for example json2ts foo.json > foo.d.ts, so the JSON file it consumes is the schema rather than the payload.
How can I create a TypeScript object from JSON?
json-schema-to-typescript produces the type, not the object. Given a schema it emits an interface such as ExampleSchema with typed properties, and you still construct the value yourself in code.
Is it possible to convert JavaScript to TypeScript?
That is not what this package does. It converts JSON Schema to TypeScript typings, and the README describes no JavaScript input path.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bcherny-json-schema-to-typescript)