mrkeyoor.com_
Sun 20 Sept 14:48 UTC
npmCLI & Toolingupdated 20 Sept 2026

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.

Verdict

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

Lab card: what happened when we installed json-schema-to-tsScreenshot of json-schema-to-ts documentation
Install✓ · 0.6s3 packages on disk · 3 MB
ImportESM import works · require() works · CommonJS package
Browser0.4 KBgzipped (0.7 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 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.

API stability5/5`FromSchema` remains the center of version 3.1.1, with behavior adjusted through explicit options for references, deserialization, defaults, `not`, and conditionals. The 3.1.1 release has no consumer API change, and 3.1.0 adds partial `unevaluatedProperties` handling without changing existing calls. The notable major-version semantic change, defaulted properties becoming required, has a named compatibility option for projects whose validators leave them absent.
Docs4/5The README shows const schemas, enums, primitive types, arrays, tuples, objects, unions, intersections, references, deserialization, extensions, and validator wrappers. It also says plainly that recursive schemas and imported JSON are unsupported, that `oneOf` is approximated as `anyOf`, and that exclusion parsing can strain TypeScript. The page is long enough that readers must hunt through examples and FAQ links instead of consulting one compact support matrix.
Maintenance3/5GitHub showed an unarchived repository with 1,778 stars, 32 open issues and pull requests together, and a push on 2026-05-09. npm still lists 3.1.1 from 2024-08-29 as latest; its only release changes concern workflows and sponsor synchronization. Repository activity suggests the project is watched, but users waiting for schema feature work have no recent package release that demonstrates a predictable delivery schedule.
Ecosystem4/5npm recorded 32,103,640 downloads for the week ending 2026-08-25. Its value comes from consuming ordinary JSON Schema objects that can also feed Ajv, Fastify, OpenAPI tooling, and API gateways. The package has only one narrow compile-time responsibility and no plugin system of its own. That makes integration easy when JSON Schema is already authoritative, while teams centered on Zod or another runtime DSL gain little from adding it.

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.
Skip it if

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

PackageRegistryPick it when
typeboxnpmUse it when TypeScript-authored schemas should come with static types and runtime validation tools.
zodnpmUse it when runtime parsing is primary and exact JSON Schema authorship is secondary.
json-schema-to-typescriptnpmUse 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.