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.
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 itA 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-ignoreanywhere 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 itOptions
[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:
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