CLI tool
JoshuaKGoldberg/TypeStat avatar
JoshuaKGoldberg/TypeStat

TypeStat: a tool for converting JavaScript to TypeScript in stages

Converts JavaScript to TypeScript and TypeScript to better TypeScript. đź§«

2,254 stars47 forksTypeScriptMIT

At a glance

What is it?
A CLI built on the automutate framework that adds types without changing runtime behaviour, then cleans up what it added, with an interactive config wizard.
Who is it for?
TypeStat's distinctive idea is sequencing rather than conversion. Most JavaScript to TypeScript tools run once and leave you with a wall of annotations; TypeStat separates fixes, from adding types, from cleanups, and lets you configure each stage, which is what makes a large codebase tractable.
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 10 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 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A tool about the sequence of edits, not the destination syntax

TypeStat describes itself as converting JavaScript to TypeScript and TypeScript to better TypeScript. The interesting word in that sentence is the second half. The README positions it as a CLI utility that modifies TypeScript types in existing code, and the list of what it can do is framed around compiler flags rather than syntax:

shell
npx typestat

Running that launches an interactive guide to creating a `typestat.json` configuration file, and after that you run `typestat --config typestat.json` to convert your files.

The four advertised capabilities are converting JavaScript files to TypeScript in a single bound, adding types on files freshly converted, inferring types to fix `--noImplicitAny` and `--noImplicitThis` violations, and annotating missing `null`s and `undefined`s to get you started with `--strictNullChecks`.

Reading that list as a sequence explains the design. You do not go from JavaScript to a strict TypeScript project in one pass. You rename the files, then quiet the noImplicitAny errors, then turn on strict null checking. Each stage is separately configurable and separately reviewable, which is what makes the approach work on a codebase large enough to be daunting.

It is MIT licensed, written in TypeScript, has 2,254 stars and 47 forks, is not archived, and the last push was on 2026-09-26.

Runtime behaviour is the constraint everything else follows from

The README states that the built-in mutators will only ever add or remove types and will never change your runtime behaviour. That sentence is the project's safety argument, and it is worth understanding how narrow and how useful it is.

Types are erased at runtime, so adding or removing them is, in principle, a no-op for behaviour. Saying so explicitly is what lets you run the tool over code you did not write, or over code you cannot fully test, and get a diff that is reviewable as a type-only change rather than a refactor.

This is also what distinguishes a mutator from a general codemod. The dependency on `automutate` in `package.json` is the mechanism: TypeStat is built on a framework for applying AST mutations, and it declares its mutators to stay inside the type-only subset. A general codemod could rewrite your control flow; this one does not.

The boundary is not entirely free of consequence. Adding a type annotation can surface a genuine type error that was previously hidden, and annotating `null`s and `undefined`s to satisfy strict null checking means telling the compiler which of several valid choices is the real one. The tool is narrowing types rather than inventing them, but the narrowing still encodes a judgement.

Six documentation pages, and the order the README gives them in

The README is almost entirely badges, so the real documentation is a short ordered list. The order is itself informative, moving from what the tool does to what it can do to how it tidies up:

`Usage.md` explains how TypeStat works. `Fixes.md` covers the type of fixes it will generate mutations for. `Cleanups.md` describes the post-fix cleaning it may apply to files. `Types.md` covers configuring how to work with types in mutations. `Filters.md` covers using tsquery to ignore sections of source files. `Custom Mutators.md` covers including or creating custom mutators.

The sequence maps onto a working session. You read Usage to understand the model, Fixes to know what is available, Types to tune it, Filters before the first run to exclude generated files, and Cleanups last because it applies afterwards.

Filters deserve particular attention. Because TypeStat edits files in place across a whole project, the ability to ignore sections using tsquery selectors is what keeps it from corrupting vendored code, generated output, or the type-heavy files where an inferred annotation would be wrong. Skipping that page is the most likely way to have a bad first experience with the tool.

For understanding the codebase itself, the README points at `./docs` in general and specifically at `docs/Architecture.md`. The repository tree confirms a substantial `docs/` directory alongside `src/`, `test/`, `bin/` and the build configuration.

Dependencies and tooling of a project run through Renovate

The `package.json` shows a small runtime dependency set. `automutate` provides the mutation framework, `@phenomnomnominal/tsquery` is the AST query selector behind the filters, `commander` parses the command line, `chalk` handles terminal output, `enquirer` drives the interactive configuration wizard, and `ts-api-utils` provides TypeScript compiler API helpers.

The build is `tsup`, and the package ships a single binary named `typestat` pointing at `bin/typestat.mjs`. Tests run under vitest, with a separate mutation testing script, and there is a `knip.json` for unused export detection.

The linting setup is extensive, which says something about the project's standards: ESLint with plugins for eslint comments, JSDoc, jsonc, package.json rules, perfectionist sorting and Node rules, plus a markdown lint with a sentences-per-line rule, a cspell configuration, Prettier, and husky for pre-commit hooks. There is also a `.release-it.json`, so releases go through release-it with conventional commits.

The release history is almost entirely Renovate dependency bumps, with occasional genuine fixes. v0.8.17 from February 2025 is a long list of dependency updates, while v0.8.16 includes a real fix to how the tsconfig is read, and v0.8.15 replaced `strip-ansi` with `stripVTControlCharacters`. The project is well maintained, though the last published release is from early 2025 despite more recent pushes.

How this compares to letting the compiler infer types

TypeScript already has an answer to most of what TypeStat does. With `allowJs` and `checkJs`, or by renaming files to `.ts`, the compiler will infer types and report errors without a separate tool. The differences are worth being concrete about.

First, TypeStat is a mutation engine. It writes inferred types into your source rather than leaving them in a declaration file, so the result is a codebase with explicit annotations that a human can then edit, rather than a build artifact that regenerates. That is the right shape when the types are meant to be maintained, and the wrong shape when they are throwaway.

Second, it works through a configuration file. That means it can be scoped to particular directories and file types, restricted by tsquery filters, and run with a defined set of mutators. For a monorepo where a blanket conversion is unacceptable, that scoping is the feature.

Third, the cleanup stage has no built-in equivalent. Files that were converted rather than freshly typed tend to accumulate unused imports, awkward type assertions, and leftovers, and a documented pass that tidies those is a real advantage.

The honest counterpoint is effort. Every stage needs configuring, and the output still needs reviewing, since a type inferred from usage is a hypothesis about intent. For a small codebase the compiler alone is faster. The tooling earns its cost where the migration is too large to do by hand.

Editorial conclusion

TypeStat's distinctive idea is sequencing rather than conversion. Most JavaScript to TypeScript tools run once and leave you with a wall of annotations; TypeStat separates fixes, from adding types, from cleanups, and lets you configure each stage, which is what makes a large codebase tractable. The promise that built-in mutators only ever add or remove types and never change runtime behaviour is the constraint that makes it safe to run over code you did not write. The trade is that it needs a configuration file describing what to do, which is more setup than a single-shot flag, though the interactive wizard handles that. Start with `npx typestat`, read the filters documentation before the first run so you can exclude generated code, and enable one mutator at a time so each diff is reviewable.

Frequently asked questions

What is TypeScript and why is it used?

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript, adding a static type system that catches a class of errors before the code runs. This repository is not the TypeScript compiler, it is a tool that uses it, adding inferred types to existing JavaScript and TypeScript files so that flags like `--noImplicitAny` and `--strictNullChecks` can be enabled.

Does TypeStat change how my code runs at runtime?

No. The README states that the built-in mutators will only ever add or remove types and never change runtime behaviour, which is what makes a diff reviewable as a type-only change. It is built on the `automutate` framework for AST mutations, so the mutators stay inside that type-only subset.

How do I start using TypeStat?

Run `npx typestat`, which launches an interactive guide to creating a `typestat.json` configuration file. After that, run `typestat --config typestat.json` to convert your files. Read the filters documentation before the first run so you can exclude generated or vendored code.

Can I make TypeStat ignore parts of my source files?

Yes. The README points to a filters documentation page describing the use of tsquery, the AST selector library TypeStat depends on, to ignore sections of source files. That is how you keep the tool away from generated code and files where inferred types would be wrong.

Official sources

  1. Issues
  2. JoshuaKGoldberg/TypeStat on GitHub
  3. License: MIT
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/joshuakgoldberg-typestat.svg)](https://hysenlabs.com/projects/joshuakgoldberg-typestat)