Skip to content

Stale @ts-ignore ​

Finds a // @ts-ignore that no longer suppresses anything — the code below it was fixed at some point, the comment just never got removed, and now it silently hides whatever DIFFERENT error might show up there next.

bash
devtoolz stale-ts-ignore [dir] [options]

Real output — a function whose body typechecks cleanly on its own, with a leftover @ts-ignore above it:

Checked 1 @ts-ignore directive.

1 stale @ts-ignore found:
  src/example.ts:2  @ts-ignore  doesn't suppress anything — the line below it typechecks cleanly without it

A genuinely necessary one — a real type error right below it — isn't flagged:

Checked 1 @ts-ignore directive.

All clear — didn't even break a sweat.

Why this doesn't duplicate @ts-expect-error ​

TypeScript itself, since 3.9, already reports an unused @ts-expect-error directive as a real diagnostic (Unused '@ts-expect-error' directive) — that's exactly why @ts-expect-error exists as the "safer" alternative in the first place. There's no equivalent built-in check for @ts-ignore, and there never will be — that gap is this command's whole reason to exist.

How it decides "stale" ​

There's no compiler API for "was this specific @ts-ignore actually needed" — nothing to just ask. The practical approach: run a real project-wide typecheck twice. Once as-is. Once against a virtual copy of the source with every @ts-ignore comment masked out (blanked to spaces of the same length, so no line numbers shift) — then compare, at each directive's own position, whether a genuinely NEW diagnostic appeared in the second run that wasn't already there in the first. If nothing new shows up, the directive wasn't suppressing anything.

This is a real ts.Program over the whole project (tsconfig.json auto-detected the same way case-check/readme-check do it), not an isolated per-file check — so it correctly understands directives that only turn out to be unnecessary once the rest of the project is taken into account.

The most expensive command here ​

Building a real project-wide program, twice, is computationally closer to running tsc --noEmit on the whole project than to any other command in this toolbox. Two things keep that from being a surprise:

  • If there isn't a single @ts-ignore anywhere in the project, the expensive part never runs at all — an instant clean report.
  • Otherwise, a status line prints before the real work starts, so a slow run on a large project never looks like a silent hang:
Running a project-wide typecheck, twice — this can take a while on a large project…

That line goes to stderr, not stdout — it never mixes into --json output.

A directive separated from its target by a blank line ​

TypeScript's own real rule: @ts-ignore only suppresses the line immediately below it — a blank line in between makes it a no-op. This command doesn't implement that rule separately; it doesn't need to. If a blank line breaks a directive, the real compiler already isn't suppressing anything there in the first (as-is) pass, so comparing that against the second pass naturally shows no new diagnostic either way — reported as stale, for the accurate reason that it currently suppresses nothing.

.vue files ​

A plain ts.Program can't include .vue files in a whole-project run at all — there's no SFC support without pulling in vue-tsc's own machinery, out of scope here. Instead, each .vue file's <script> block gets an isolated single-file check, the same virtual-file technique readme-check uses for its own code blocks — real imports still resolve against the real package on disk, but a diagnostic that would only show up from some OTHER file's use of this one isn't visible. Same accepted, documented limitation as readme-check's own isolated checking.

Checked 1 @ts-ignore directive.

1 stale @ts-ignore found:
  src/Widget.vue:7  @ts-ignore  doesn't suppress anything — the line below it typechecks cleanly without it

Options ​

[dir] ​

Package directory to check (default: .).

--tsconfig <path> ​

tsconfig.json to read compiler options from — auto-detected by default.

--ignore <glob> ​

Extra ignore pattern (repeatable), on top of the built-in defaults — applies to .vue file discovery only (real TS/JS project files come from tsconfig.json itself).

--no-respect-gitignore ​

Don't also honor the project's .gitignore for .vue discovery.

Example:

bash
devtoolz stale-ts-ignore                          # tsconfig.json in the current directory
devtoolz stale-ts-ignore path/to/package           # a specific directory
devtoolz stale-ts-ignore --tsconfig tsconfig.build.json