npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

json-type-extractor

v0.1.0

Published

Stream a JSON file and emit a TypeScript declaration (.d.ts) describing its structure. Designed for files larger than 50 MB.

Readme

json-type-extractor

Turn real JSON into useful TypeScript declarations — in one command.

Stream a file from disk, infer the structure it actually contains, and produce a deterministic .d.ts without loading the complete JSON document into memory.

npm version Node.js License: MIT

npx json-type-extractor@latest ./data.json

JSON file → streaming inference → review-friendly TypeScript

Features

Built for the moment when a JSON file is real, useful, and too large or unfamiliar to model by hand.

| What you get | Why it matters | | --- | --- | | Zero-install CLI | Go from a JSON file to declarations immediately with npx. | | Streaming input | Parse from disk without loading the complete source document. | | Useful literal unions | Keep observed sets such as "draft" \| "published" instead of widening everything to string. | | Named variants | Turn heterogeneous objects with fields such as type or kind into readable discriminated unions. | | Deterministic output | Sorted properties and literal values keep generated diffs stable and reviewable. | | Deep-schema-safe emission | Emit deeply nested inferred schemas through an iterative traversal. | | ESM library API | Reuse the same inference and emission pipeline in TypeScript or JavaScript. |

Quick Start

npx json-type-extractor@latest ./data.json

That is the complete setup. The command writes data.json.d.ts next to the input file.

From this JSON

{
  "id": 1,
  "environment": "prod",
  "debugMode": true,
  "features": ["search", "export"]
}

To these declarations

The generated file starts with a generated-file comment, followed by:

declare module "data.json" {
  export interface Data {
    debugMode: boolean;
    environment: "prod";
    features: Array<"export" | "search">;
    id: number;
  }

  const value: Data;
  export default value;
}

[!NOTE] The declaration describes the values observed in the input. It is not a validation schema and does not guarantee the shape of future data.

CLI Reference

npx json-type-extractor@latest <input.json> [options]

| Option | Description | Default | | --- | --- | --- | | -o, --out <path> | Write the declaration to a custom path. | <input>.d.ts | | -r, --root <name> | Name the exported root type. | Input filename stem in PascalCase | | -m, --module <name> | Set the declare module "<name>" specifier. | Bare input filename | | --no-module | Emit a plain declaration without a module wrapper. | Module wrapper enabled | | --no-string-literals | Disable literal narrowing and discriminant-based variant naming. | Literal narrowing enabled | | --string-literals-max <N> | Set the maximum distinct string literals retained per field before falling back to string. | 8 | | -h, --help | Show CLI help. | — |

If --out points into another directory, that destination directory must already exist.

Common recipes

Choose the output location and root type:

npx json-type-extractor@latest ./users.json --out ./types/users.d.ts --root Users

Emit a plain declaration without a module wrapper:

npx json-type-extractor@latest ./events.json --no-module

Match an application import specifier:

npx json-type-extractor@latest ./config.json --module "@app/config"

Control string literal inference:

npx json-type-extractor@latest ./events.json --no-string-literals
npx json-type-extractor@latest ./events.json --string-literals-max 16

Output Behavior

Module declarations

By default, the declaration is wrapped in declare module "<filename>", where the module name is the input's bare filename. For example, ./data/data.json uses "data.json". Use --module <name> to choose an explicit specifier or --no-module to emit a plain .d.ts file.

A bare module name does not automatically match every relative JSON import. Choose --module or --no-module to fit the imports and TypeScript configuration in your project.

String literal narrowing

String values are narrowed to a literal or literal union while no more than 8 distinct values have been observed at that schema node. Once the cap is exceeded, that node falls back to string. Use --string-literals-max <N> to change the cap, or --no-string-literals to emit all observed strings as string and disable discriminant-based variant naming.

Discriminated object variants

When heterogeneous objects have different key sets, the emitter looks for a shared single-literal string field with a distinct value in each variant. Candidate fields are tried in this exact order: type, kind, _type, __typename, then qualifying common fields in alphabetical order. Their literal values become interface names, producing unions such as:

export interface Leaf {
  type: "leaf";
  value: number;
}

export interface Branch {
  children: unknown[];
  type: "branch";
  value: number;
}

export interface Tree {
  nodes: Array<Leaf | Branch>;
}

Objects with identical key sets merge into one shape instead of separate variants. Their observed field values are merged too, so a shared discriminant can become a literal union.

Programmatic API

Need the pipeline inside your own tooling? Install the package:

npm install json-type-extractor

The library API is ESM-only. Use package-root imports:

import { extractTypes } from "json-type-extractor";

const result = await extractTypes({
  input: "users.json",
  output: "users.json.d.ts",
  rootName: "Users",
  moduleName: "users.json",
  useStringLiterals: true,
  stringLiteralCap: 8,
});

result contains the written outputPath, inferred schema, and generated declaration.

Want control over the two stages? Run inference and emission separately:

import { emitDeclaration, inferSchema } from "json-type-extractor";

const schema = await inferSchema("users.json", {
  useStringLiterals: true,
  stringLiteralCap: 8,
});

const declaration = emitDeclaration(schema, {
  rootName: "Users",
  moduleName: "users.json",
  useStringLiterals: true,
});

The package also exports Schema, defaultOutputPath, defaultRootName, and defaultModuleName from its public entry point.

How It Works

json-type-extractor feeds the file through a stream-json token stream. As tokens arrive, it accumulates observed primitive types, object key-set shapes, array element schemas, and bounded string-literal sets. Object shapes with the same structure are deduplicated; an iterative, deterministic emitter then turns the result into a declaration.

Parsing is streamed, but memory use still depends on the complexity of the inferred schema and the number of unique shapes and observed values retained within their configured bounds.

Requirements

  • Node.js 18 or newer.
  • A regular JSON file path as input.
  • ESM when using the library API.

Limitations

  • Output is inferred from observed data; it does not validate future values and is not JSON Schema.
  • Input from stdin, URLs, and gzip files is not supported.
  • Parent directories for an output path are not created automatically.
  • The default bare module specifier may not match project imports; use --module or --no-module when needed.

Maintainer documentation

See the npm publishing guide for the release workflow, first-package bootstrap, and Trusted Publishing setup.

License

Released under the repository's MIT License.