json-schema-to-ts review
json-schema-to-ts 3.1.1 turns a JSON Schema written as a TypeScript literal into a static type through `FromSchema`. It understands object requirements, enums, arrays, tuples, unions, finite references, defaults, selected conditionals, and deserialization mappings. It never checks runtime input; Ajv or another validator still does that job. Release 3.1.1 only updates repository workflows and sponsor synchronization. The prior 3.1.0 release added partial `unevaluatedProperties` support. Our Node 22 checks found bundled declarations and working `require()` plus ESM import interop.
json-schema-to-ts 3.1.1 installed in 0.6 seconds, occupied 3 MB, and produced a 0.4 KB gzipped browser bundle in our sandbox with 0 audit findings. Use it when a non-recursive JSON Schema literal is already the contract; choose code generation for imported JSON or a runtime schema library when validation should come from the same API.
We installed it
| Install | ✓ · 0.6s | 3 packages on disk · 3 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | 0.4 KB | gzipped (0.7 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does json-schema-to-ts install cleanly?
Yes. In a fresh container with an empty cache, npm install json-schema-to-ts finished in 0.6s, leaving 3 packages and 3 MB on disk. npm audit reported no known vulnerabilities.
How much does json-schema-to-ts add to a browser bundle?
0.4 KB gzipped (0.7 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does json-schema-to-ts work with both ESM and CommonJS?
Yes. Both import 'json-schema-to-ts' and require('json-schema-to-ts') worked in Node 22 in our run. The package is published as CommonJS.
Does json-schema-to-ts include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
json-schema-to-ts or typebox: which should you use?
typebox: Use it when TypeScript-authored schemas should come with static types and runtime validation tools. json-schema-to-ts 3.1.1 installed in 0.6 seconds, occupied 3 MB, and produced a 0.4 KB gzipped browser bundle in our sandbox with 0 audit findings.
When should you not use json-schema-to-ts?
You need parsing or runtime validation from this dependency. FromSchema exists only in the type system, while Zod or TypeBox can pair definitions with executable checks.
Use it if
- JSON Schema already owns an OpenAPI, Fastify, gateway, or cross-language contract and a second handwritten interface keeps drifting.
- Ajv or another validator handles runtime input while TypeScript needs the matching compile-time result.
- Schemas can live in `.ts` files as literal objects checked with `satisfies JSONSchema`.
- The schema graph uses finite local or external references and does not point recursively back to itself.
- You need parsing or runtime validation from this dependency. `FromSchema` exists only in the type system, while Zod or TypeBox can pair definitions with executable checks.
- Schemas arrive through JSON imports. The README says TypeScript cannot apply `as const` to an imported JSON value, so this workflow loses the literal information inference needs.
- The contract is recursive, such as a node whose children reference the node schema. Recursive schema expansion is a documented unsupported case.
- Static `oneOf` must reject values matching multiple branches. The package treats `oneOf` like `anyOf`, which cannot express that exclusivity in TypeScript.
- The editor already struggles with deep conditional types. Parsing `not` and `if/then/else` is opt-in because complex schemas can reach TypeScript's instantiation limit.
- You need generated `.d.ts` files from external schema files. `json-schema-to-typescript` fits a build-time code-generation workflow better.
Setup reality
Our json-schema-to-ts 3.1.1 install took 0.6 seconds under Node 22. It left 3 packages using 3 MB, and npm audit found 0 vulnerabilities across critical, high, moderate, and low severities. The package declares 2 direct dependencies and 0 peers, reports 1200 KB unpacked, requires Node 16 or newer, uses MIT, and includes its TypeScript declarations.
There are no credentials, native extensions, generated files, or required configuration. The README requires TypeScript 4.3 or newer with strict checking. Author each schema in a TypeScript module and preserve keyword values with as const; on TypeScript 4.9 or later, as const satisfies JSONSchema also checks the schema at its definition. An imported .json value is already widened, and TypeScript does not allow fixing that afterward with as const.
The npm artifact is CommonJS and has no exports map. Both require() and ESM import worked in our container. Importing the whole package into an esbuild browser entry produced 0.7 KB minified and 0.4 KB gzipped. Prefer import type for FromSchema and JSONSchema so inference adds no runtime edge. The asConst and validator-wrapper helpers are real functions and remain in emitted code when used.
FromSchema gives no assurance about an unknown value until a validator accepts it. The package can wrap an Ajv-style compiler as a type guard so validation narrows the result. Defaults become required in the inferred type unless keepDefaultedPropertiesOptional is true, which must match whether the validator inserts defaults. Recursive $ref graphs remain unsupported, and expensive not or conditional parsing stays disabled unless explicitly requested.
Patterns
Infer an object contract infer-object
import type { FromSchema } from 'json-schema-to-ts';
const userSchema = {
type: 'object',
properties: {
id: { type: 'integer' },
email: { type: 'string' },
},
required: ['id'],
additionalProperties: false,
} as const;
type User = FromSchema<typeof userSchema>;`as const` preserves literal keywords and required-property names. Without it, TypeScript widens the object and the inferred type loses precision.
Check the schema where it is written check-schema
import type { FromSchema, JSONSchema } from 'json-schema-to-ts';
const userSchema = {
type: 'object',
properties: { id: { type: 'integer' } },
required: ['id'],
} as const satisfies JSONSchema;
type User = FromSchema<typeof userSchema>;`satisfies` requires TypeScript 4.9 or newer. It checks and autocompletes the schema without widening its literal values.
Remove undeclared object keys close-object
const closedSchema = {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name'],
additionalProperties: false,
} as const;
type Closed = FromSchema<typeof closedSchema>;JSON Schema object properties are open unless constrained. `additionalProperties: false` removes the inferred unknown-key signature.
Create a literal union from enum infer-enum
const stateSchema = {
enum: ['queued', 'running', 'complete'],
} as const;
type State = FromSchema<typeof stateSchema>;
// 'queued' | 'running' | 'complete'The enum array must stay readonly through `as const`. A widened `string[]` cannot produce the three-member union.
Describe a fixed numeric pair infer-tuple
const pointSchema = {
type: 'array',
items: [{ type: 'number' }, { type: 'number' }],
minItems: 2,
maxItems: 2,
additionalItems: false,
} as const;
type Point = FromSchema<typeof pointSchema>;Tuple bounds rely on strict null checks, which the package expects through TypeScript strict mode.
Resolve an internal definition resolve-local-ref
const responseSchema = {
definitions: {
userId: { type: 'integer' },
},
type: 'object',
properties: {
ownerId: { $ref: '#/definitions/userId' },
},
required: ['ownerId'],
} as const;
type Response = FromSchema<typeof responseSchema>;Finite local references are supported. A definition that eventually refers to itself is a recursive graph and cannot be expanded by `FromSchema`.
Provide an external reference resolve-external-ref
const userSchema = {
$id: 'https://example.test/user.json',
type: 'object',
properties: { id: { type: 'integer' } },
required: ['id'],
} as const;
const usersSchema = {
type: 'array',
items: { $ref: 'https://example.test/user.json' },
} as const;
type Users = FromSchema<typeof usersSchema, {
references: [typeof userSchema];
}>;An external schema needs an `$id` that matches the reference and must appear in the `references` tuple passed to `FromSchema`.
Keep an unfilled default optional keep-default-optional
const settingsSchema = {
type: 'object',
properties: {
theme: { type: 'string', default: 'light' },
},
additionalProperties: false,
} as const;
type Settings = FromSchema<typeof settingsSchema, {
keepDefaultedPropertiesOptional: true;
}>;Defaulted properties are required in the inferred type unless this option is enabled. Match it to the runtime validator's default-insertion setting.
Model a runtime date conversion map-deserialized-date
type Event = FromSchema<typeof eventSchema, {
deserialize: [{
pattern: { type: 'string'; format: 'date-time' };
output: Date;
}];
}>;`deserialize` changes the static output type only. A parser or validator still has to construct the `Date` at runtime.
Opt into exclusion inference enable-not-keyword
type Allowed = FromSchema<typeof schema, {
parseNotKeyword: true;
}>;`not` parsing is off by default because complex exclusions can exhaust TypeScript's instantiation depth and collapse to `any`.
Wrap an Ajv compiler as a type guard narrow-ajv-result
import Ajv from 'ajv';
import {
wrapCompilerAsTypeGuard,
type $Compiler,
} from 'json-schema-to-ts';
const ajv = new Ajv();
const $compile: $Compiler = (schema) => ajv.compile(schema);
const compile = wrapCompilerAsTypeGuard($compile);
const isUser = compile(userSchema);
if (isUser(input)) {
console.log(input.id);
}Ajv performs the runtime check. The wrapper supplies the TypeScript predicate that narrows `input` after validation succeeds.
Narrow a schema with the helper use-as-const-helper
import { asConst } from 'json-schema-to-ts';
import type { FromSchema } from 'json-schema-to-ts';
const sizeSchema = asConst({
type: 'string',
enum: ['small', 'large'],
});
type Size = FromSchema<typeof sizeSchema>;`asConst` is a runtime identity function and leaves an emitted import. Language-level `as const` keeps the dependency type-only.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| typebox | npm | Use it when TypeScript-authored schemas should come with static types and runtime validation tools. |
| zod | npm | Use it when runtime parsing is primary and exact JSON Schema authorship is secondary. |
| json-schema-to-typescript | npm | Use it to generate committed declarations from external JSON Schema files. |
More cli & tooling guides
commander · chalk · typescript · esbuild · yargs · click · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

