Skip to content

ycmjason/ts-migrating

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

30 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

@ts-migrating โ€” Progressively Upgrade tsconfig.json

๐Ÿš€ TypeScript keeps evolving โ€” and your tsconfig should too.

This plugin lets you upgrade to your desired compilerOptions (e.g. strict, noUncheckedIndexedAccess, erasableSyntaxOnly, checkJs) across your entire codebase, while letting problematic lines fall back to the old compilerOptions.

Upgrading tsconfig often breaks existing code, and fixing all errors at once is unrealistic.

@ts-migrating helps your team migrate to a desired tsconfig gradually and safely.

I chose @ts-migrating (rather than @ts-migration) to better reflect the pluginโ€™s progressive and incremental philosophy.

๐Ÿ™‹โ€โ™€๏ธ Why not @ts-expect-error / @ts-ignore?

Using @ts-expect-error or @ts-ignore to silence TypeScript errors can work in the short term โ€” but they come with trade-offs:

  • They suppress all errors on the line, not just those introduced by the new compilerOptions. This can hide unrelated issues and introduce technical debt.
  • There are cases where you actually want to use @ts-expect-error and @ts-ignore. Mixing their real usages with tsconfig migration is ๐Ÿคฎ.

This plugin takes a different approach: it lets you apply the desired compilerOptions globally while allowing them to be reverted line-by-line. This keeps your code clean, and your intent clear โ€” enabling a safer and more maintainable upgrade path.

๐Ÿค– How does this work?

@ts-migrating is a TypeScript plugin that lets you enable your target tsconfig during development (in IDEs or editors that use the TypeScript Language Service) and in CI โ€” without affecting tsc or your production build.

The philosophy behind the plugin follows three simple steps:

  1. ๐Ÿ›‘ Prevention

    • Errors from your target config are surfaced during development and in CI.
    • This ensures no new violations are introduced into the codebase.
  2. ๐Ÿ”ง Reduction

    • Lines marked with @ts-migrating will be typechecked with your original tsconfig. Ensuring type-safety throughout.
    • Developers can progressively fix these lines, reducing violations over time.
  3. โœ… Migration

    • Once all violations are fixed, no @ts-migrating directives remain.
    • At this point, you're ready to fully adopt the new tsconfig โ€” and the plugin has served its purpose.

๐Ÿ“š Overview

@ts-migrating consists of two parts:

  1. ๐Ÿ”Œ TypeScript Language Service Plugin

    • Enables IDEs to show errors from the tsconfig you're migrating to.
    • Revert lines marked with @ts-migrating to be type-checked with your original tsconfig.
  2. ๐Ÿ–ฅ๏ธ Standalone CLI: ts-migrating

    • ts-migrating check

      • Run @ts-migrating-aware type checking using your new tsconfig.
      • Pass --reporter json (or ndjson) to emit a machine-readable report for CI gates and dashboards (see ๐Ÿ“Š JSON reporting).
    • ts-migrating annotate

      • Automatically mark all errors caused by your new tsconfig with @ts-migrating.
      • โš ๏ธ Run this with a clean git state!!! This script will automatically add the @ts-migrating directive above every line with TypeScript error introduced by your new tsconfig. Please review the changes carefully. It is recommended to run your formatter and linter afterwards. You may need to run this command again after formatter / linter.๏ธ

๐ŸŽช Examples

๐Ÿ“ฆ Install and Setup

This project does NOT require any IDE extensions. It relies purely on TypeScript's own Language Service, so it works on most IDEs and editors that support TypeScript (e.g., VSCode, WebStorm).

To install:

cd my-cool-project
npm install -D ts-migrating

In your existing tsconfig.json, add the plugin:

{
  // ...
  "compilerOptions": {
    // ...
    "plugins": [
      {
        "name": "ts-migrating",
        "compilerOptions": {
          // ... put the compiler options you wish to migrate to, for example:
          "strict": true
        }
      }
    ]
    // ...
  }
  // ...
}

โ„น๏ธ Note: plugins only affect the TypeScript Language Service (used by IDEs). They do not impact tsc or your build.

๐ŸŽ‰ Your codebase is now ready!

โœ… Verify the Setup

๐Ÿง‘โ€๐Ÿ’ป In your IDE

  • Restart the IDE, or just the TS server.
  • Confirm that type errors now reflect the new compilerOptions. For example, when migrating to strict mode, verify that strict-specific errors appear.
  • Add // @ts-migrating before a line with an error โ€” the error should disappear in the IDE.

๐Ÿ–ฅ In the terminal

  • Run:

    npx ts-migrating check

    You should see errors from the new config, excluding those marked with @ts-migrating.

    ๐Ÿ’พ Running out of memory? check and annotate type-check your whole project, so on large codebases they can exceed Node's default heap (you'll see JavaScript heap out of memory). Give Node a bigger heap by passing the same --max-old-space-size flag Node uses (in MB):

    npx ts-migrating check --max-old-space-size=8192

โœจ Optional Next Steps

  • Run npx ts-migrating annotate to automatically annotate newly introduced errors with // @ts-migrating.
  • Replace your CI type-check step with npx ts-migrating check to prevent unreviewed errors from slipping through.

๐Ÿ“Š JSON reporting

For CI gates and dashboards, check can emit a machine-readable report instead of the human-readable output:

npx ts-migrating check --reporter json    # one JSON array
npx ts-migrating check --reporter ndjson  # one JSON object per line (streamable)

stdout carries one entry per diagnostic (progress logs go to stderr, so stdout stays pure). The flat shape is easy to jq/group/count or convert to CSV. With json you get a single array:

[
  {
    "file": "src/one.ts",                    // relative to the current working directory
    "position": {                            // 1-based line/column; null for file-level diagnostics
      "start": { "line": 32, "column": 10 },
      "end": { "line": 32, "column": 18 }
    },
    "code": 7006,                            // the TypeScript error code
    "message": "Parameter 'x' implicitly has an 'any' type.",
    "origin": "ts-migrating",                // "ts-migrating" = introduced by your target tsconfig; "baseline" = already present (current tsconfig + other plugins)
    "markedWithTsMigratingDirective": false  // true = suppressed by a @ts-migrating directive (i.e. migration debt)
  }
]

--reporter ndjson emits the exact same records, but one JSON object per line (NDJSON) instead of an array. Prefer it on large repos: it streams (neither ts-migrating nor your consumer has to hold the whole report in memory) and pipes line-by-line into jq -c, grep, or wc -l.

This gives you everything to build your own metrics, for example:

  • Track remaining migration debt โ€” count entries where markedWithTsMigratingDirective is true. This is the number that trends down to zero as you migrate (the unmarked ones are kept at 0 by your CI gate).
  • Fail CI on regressions โ€” compare the debt count against the base branch and fail if it goes up.
  • Break down by error code โ€” group origin: "ts-migrating" entries by code to see what to tackle first.
# remaining migration debt
npx ts-migrating check --reporter json | jq '[.[] | select(.markedWithTsMigratingDirective)] | length'

# unmarked ts-migrating errors grouped by error code
# (filter *before* grouping so marked debt and baseline errors don't leak in)
npx ts-migrating check --reporter json \
  | jq 'map(select(.origin == "ts-migrating" and (.markedWithTsMigratingDirective | not)))
        | group_by(.code)[] | { code: .[0].code, count: length }'

# streaming: count remaining debt line-by-line, nothing held in memory
npx ts-migrating check --reporter ndjson \
  | jq -c 'select(.markedWithTsMigratingDirective)' | wc -l

โ„น๏ธ The command still exits non-zero when there are unmarked ts-migrating errors (and, with --all-type-errors, when there are pre-existing baseline errors), so it can both gate CI and produce the report. The JSON is printed regardless of the exit code.

โ„น๏ธ Stale (unused) @ts-migrating directives are reported too โ€” as unmarked ts-migrating entries with code 555 โ€” so the JSON gate fails on them exactly like the default check. Filter them out with select(.code != 555) if you only want real type errors.

API

You can use this project programmatically. This can be useful if you would like to have custom integrations, for example: reporting error counts to dashboard etc.

The functions are exposed via ts-migrating/api:

  • getTsMigratingReportForFile โ€” returns one entry per diagnostic for a file, each tagged with its origin (ts-migrating vs baseline) and whether it is markedWithTsMigratingDirective. This powers the JSON reporter and, unlike getSemanticDiagnosticsForFile, also surfaces the marked errors (your migration debt), which the language service otherwise hides.

    import { getTsMigratingReportForFile } from 'ts-migrating/api';
    
    const report = getTsMigratingReportForFile('path/to/file.ts');
    const debt = report.filter(e => e.markedWithTsMigratingDirective).length;
  • getSemanticDiagnosticsForFile and isPluginDiagnostic โ€” the raw diagnostics for a file (excluding marked lines):

    import { getSemanticDiagnosticsForFile, isPluginDiagnostic } from 'ts-migrating/api';
    
    getSemanticDiagnosticsForFile('path/to/file.ts') // returns all diagnostics using your new tsconfig, including non-plugin ones
      .filter(isPluginDiagnostic) // removes all non-plugin diagnostics

You could technically also import from ts-migrating/cli and ts-migrating (the ts plugin itself) too.

๐Ÿ“ฃ Shoutout

This project wouldn't be possible without inspiration from:

๐Ÿ‘ค Author

YCM Jason

About

This plugin lets you upgrade to your desired compilerOptions (e.g. strict, noUncheckedIndexedAccess, erasableSyntaxOnly) across your entire codebase, while letting problematic lines fall back to the old compilerOptions.

Topics

Resources

Stars

Watchers

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages