json-schema-to-typescript
json-schema-to-typescript turns JSON Schema files or JavaScript schema objects into TypeScript declaration text. Its json2ts CLI accepts JSON, YAML, stdin, files, directories, and globs; its API exposes compile and compileFromFile for build scripts. It resolves local and remote $ref links, emits interfaces, unions, enums, tuples, comments, and index signatures, then formats the result with Prettier by default. The output describes values to TypeScript but does not validate a single value at runtime.
A practical generator when JSON Schema owns the contract and erased validation rules are understood. Do not mistake readable .d.ts output for runtime safety or full JSON Schema equivalence.
Use it if
- JSON Schema is already the source of truth and TypeScript consumers need generated declarations kept in sync
- You need a CLI that can convert a directory or quoted glob of JSON and YAML schemas into matching .d.ts files
- Your schemas use definitions, $defs, local or external $ref links, enums, unions, intersections, or bounded arrays that hand-written interfaces would duplicate
- You want a programmable compiler with naming, formatting, reference-resolution, and output strictness options
- You need runtime validation: generated interfaces disappear after compilation, so malformed API input still passes unless Ajv or another validator runs separately
- Your correctness depends on constraints TypeScript cannot represent: the README lists format, pattern, minimum, maximum, multipleOf, uniqueItems, property counts, and not among the semantics omitted from generated types
- You rely on oneOf as exclusive-or: the feature table says oneOf is treated like anyOf, so a value matching multiple branches is not excluded by the TypeScript union
- You need complete support for every JSON Schema draft or extension: patternProperties is only partial, custom extensions are unchecked unless mapped with the project-specific tsType escape hatch, and validateRequired is not supported
- You compile untrusted schemas with unrestricted external $ref links: reference resolution can read files or networks according to parser options, so isolate the build or restrict resolvers before accepting third-party input
Setup reality
Install the package locally and run its json2ts binary with npx json2ts; npx json-schema-to-typescript is not the documented command. Version 15.0.4 requires Node 16 or newer and brings nine runtime dependencies, including Prettier 3, js-yaml, lodash, tinyglobby, and json-schema-ref-parser. There is no required config file. CLI options are flags, including nested flags such as --style.singleQuote, while programmatic callers pass an options object. Input may be JSON or YAML. With stdin or a single file, use stdout redirection or an explicit output path so generated declarations land where expected. Quote globs so the tool expands them consistently, and send directory or glob input to an output directory; the CLI source explicitly rejects a multi-file input paired with one .d.ts output. Reference paths depend on cwd: compileFromFile automatically bases them on the input file's directory, while compile on an object defaults to process.cwd() unless overridden. External references are declared by default and may involve network access. Schemas without additionalProperties explicitly set are treated as open because the package default is true, often producing an index signature broader than teams expect. Set additionalProperties: false in the schema when closed objects are intended. Definitions not reachable from the root are omitted unless unreachableDefinitions is enabled. Bounded arrays can expand into unions of tuple lengths up to the maxItems threshold of 20; ignoreMinAndMaxItems or maxItems controls that precision and cost. Formatting is on by default and uses bundled Prettier settings. The README warns that Prettier can be slow enough to crash on giant output, so use format: false for large generation jobs and format once afterward. Generated TypeScript is an artifact, not a replacement for schema tests: diff it in CI and keep a real JSON Schema validator on data boundaries.
Patterns
Generate a declaration from one schemagenerate-interface
npx json2ts --input schemas/user.json --output generated/user.d.tsThe executable is json2ts. Keep the JSON Schema as the edited source and regenerate the .d.ts file instead of changing generated output by hand.
Compile a schema through stdin and stdoutpipe-stdin
cat schemas/user.json | npx json2ts > generated/user.d.tsShell redirection makes the destination unambiguous. Errors go to stderr and the CLI exits nonzero when parsing or generation fails.
Compile a YAML schemacompile-yaml-schema
npx json2ts schemas/order.yaml generated/order.d.tsYAML support uses js-yaml. The resulting type has the same semantic limits as JSON input, including erased validation-only constraints.
Generate matching files from a directorycompile-schema-directory
npx json2ts --input schemas --output generated/typesDirectory input is recursive and expects an output directory. Do not pair multi-file input with a single .d.ts destination.
Generate declarations from a globcompile-schema-glob
npx json2ts --input 'schemas/**/*.schema.json' --output generated/typesQuote the glob so json2ts, rather than the shell, discovers the files. An unmatched pattern is treated as an error.
Compile a schema object in a build scriptcompile-schema-object
import {compile} from 'json-schema-to-typescript';
import {writeFile} from 'node:fs/promises';
const schema = {
type: 'object',
additionalProperties: false,
properties: {id: {type: 'string'}, active: {type: 'boolean'}},
required: ['id'],
};
const source = await compile(schema, 'Account');
await writeFile('generated/account.d.ts', source);compile needs an explicit root type name. additionalProperties: false prevents the generated interface from accepting arbitrary extra keys.
Compile a file with local referencescompile-schema-file
import {compileFromFile} from 'json-schema-to-typescript';
import {writeFile} from 'node:fs/promises';
const source = await compileFromFile('schemas/invoice.json');
await writeFile('generated/invoice.d.ts', source);compileFromFile bases reference resolution on the schema file's directory, which is safer than relying on the process working directory.
Set the $ref resolution rootset-reference-root
const source = await compile(schema, 'Invoice', {
cwd: new URL('./schemas/', import.meta.url).pathname,
declareExternallyReferenced: true,
});Remote and file references are dereferenced by json-schema-ref-parser. Restrict its $refOptions resolvers when schemas are not fully trusted.
Emit definitions that the root does not referenceemit-unused-definitions
npx json2ts -i schemas/catalog.json -o generated/catalog.d.ts --unreachableDefinitionsunreachableDefinitions defaults to false. Enable it only when consumers import standalone definitions that are not reachable from the root schema.
Generate stricter index signaturestighten-index-signatures
const source = await compile(schema, 'Dictionary', {
strictIndexSignatures: true,
additionalProperties: false,
});The option appends undefined to index-signature values for stricter access. It does not close schemas that explicitly permit additional properties.
Avoid tuple explosions from array boundscontrol-bounded-arrays
const source = await compile(schema, 'Report', {
ignoreMinAndMaxItems: true,
maxItems: 20,
});With bounds enabled, arrays may become unions of tuple lengths. Ignore bounds when a simpler T[] is preferable to exact compile-time length.
Skip built-in formatting for a large schemaspeed-up-large-generation
const source = await compile(largeSchema, 'Catalog', {
format: false,
bannerComment: '/* generated file */',
});The README identifies Prettier as a failure and performance point on giant output. Format once in a later build step if readable generated code is still required.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| quicktype | npm | You need types or models in several languages and may start from sample JSON as well as JSON Schema |
| json-schema-to-ts | npm | Schemas live as TypeScript const values and you want inferred types without generating .d.ts files |
| @sinclair/typebox | npm | A TypeScript-first codebase wants schemas and static types created together from one builder API |
| @hey-api/openapi-ts | npm | The source is an OpenAPI document and you need clients, SDKs, and service types rather than generic schema declarations |