About
exists because any is the one TypeScript type that switches the type checker off. Every as any is a place where a refactor can break production without a single red squiggle. The name is the joke; the tool is serious.
Why any matters
any is contagious. A value typed any keeps that type through property access, calls and assignments, so one cast at an API boundary silently removes checks from everything downstream. Errors that TypeScript would have caught at compile time turn into undefined is not a function at runtime.
The fix is nearly always cheap: unknown where the shape is genuinely unknown (it forces a check before use), an interface where the shape is known, and a type guard or schema validator at the boundary where data comes in.
What counts
The scanner parses every .ts, .tsx, .mts and .cts file with the TypeScript compiler API and walks the syntax tree. Only an any type node counts: the word in a comment, a string or a variable called any does not. Each finding is classified by where it sits in the tree:
- as anyA type assertion to any. It switches type checking off for that expression and everything derived from it.
- : anyA variable, parameter, property, return type or alias declared as any.
- Foo<any>any used as a type argument: Promise<any>, Map<string, any>, useState<any>(), extends Base<any>.
- any[]An array of any: any[], Array<any> or ReadonlyArray<any>.
- Record<K, any>Record<K, any>: an object whose values are unchecked.
- implicit anyA parameter without a type in a declared function. noImplicitAny reports these as errors.
- otherany in a union, tuple, constraint, mapped or conditional type, keyof any, and similar places.
Limits of the method
- Implicit any is approximated. Running the full type checker over an arbitrary repository (with its dependencies) is too expensive, so the scanner only flags untyped parameters in places where TypeScript never infers them: function and method declarations, constructors, and function expressions assigned to an untyped variable. Untyped callbacks passed as arguments are skipped because their types usually come from context. Inferred
anyfrom untyped imports or JSON is not detected at all. - Only TypeScript files. JavaScript files (also with JSDoc types) are not scanned. Declaration files (
.d.ts) are skipped unless you tick the box, because they often describe someone else's untyped code. - Generated and vendored code is skipped.
node_modules,dist,build,out,coverage,vendor,.nextand similar folders are never entered. Files over 2 MB (bundles) are skipped. - Suppressions are counted anyway. An
eslint-disablecomment does not remove anany, so it still counts. Sometimes ananyis the right call (deep in a type-level utility, say); the fix suggestions are rules, not judgement. - Size. Archives over 50 MB and repositories with more than 20,000 TypeScript files are refused or truncated, and at most 10,000 findings per scan are listed (the total is still counted).
- Density is findings per 1,000 lines across the scanned files, blank lines included. It makes repositories of different sizes comparable; it does not say anything about severity.
Automatic scanning
Besides the scans visitors start, a small crawler discovers popular public TypeScript repositories through GitHub's search API (recently pushed popular repositories, new repositories gaining stars, and a rotating set of topics such as nextjs, react and nodejs) and scans a handful of them every twenty minutes. A repository is only scanned again when its default branch has moved since the last scan, so the traffic stays small: a few GitHub API requests per run, well within the limits GitHub publishes. Archived repositories, forks, templates and empty repositories are skipped. The results feed the trending page and each repository's history page.
Only repository metadata (name, stars, default branch, push date) and the code snippets around each finding are stored. No commit authors, contributor names or e-mail addresses are fetched or shown.
Opting out
Maintainers who would rather not see their repository here can opt out in either of two ways:
- add an empty
.fasanyignorefile to the repository root, or - add
"fasany": { "optOut": true }to the rootpackage.json.
The next time the repository is scanned, by the crawler or by a visitor, the opt-out is noticed, every stored scan and finding for it is deleted, and it is not scanned again. For an immediate removal, or if you want a repository excluded without changing it, write to hbouma01@gmail.com.
Badges, resolving and rate limits
/badge/owner/repo.svg shows the count from the latest finished scan of that repository and is cached for an hour. Scan again after fixing to update it.
Signed-in users can mark a finding resolved, optionally with the pull request link. That only records progress on this site; it does not change the repository. The leaderboard counts resolved findings per person, by display name.
Scans use GitHub's API, which allows a limited number of requests per hour, and each visitor can start a handful of scans per quarter hour. A scan of the same repository and ref within ten minutes returns the existing result. Everything is deleted after 30 days.