DEV Community

Jacob Palomo
Jacob Palomo

Posted on

Stop Type Erosion: Building a Zero-Dependency Type-Health Auditor for TypeScript 🎯

We've all seen this lifecycle: a project starts with strict: true, pristine interfaces, and zero type bypasses. Six months later, under feature delivery pressure, workarounds like as any, @ts-ignore, non-null assertions (!), and unvalidated JSON.parse calls start creeping in.

The code still compiles, but runtime reliability plummets. This pattern is known as Type Erosion.

To solve this, I built Any-Hunter — a zero-dependency CLI, programmatic library, and GitHub Action that audits your codebase and computes a Type-Health Score (0–100).


🔍 Why Not Just ESLint?

Standard linters inspect static syntax, but Any-Hunter approaches typing from an architectural health perspective:

  1. A Measurable Health Metric: Instead of an endless log of warnings, you get a concrete score (e.g., Type-Health: 91/100) that management and teams can track over time.
  2. Semantic Compiler Analysis: Uses TypeScript's TypeChecker to catch Invisible anys (variables implicitly inheriting any from untyped APIs).
  3. Scope-Aware Severity: A local variable with any is a warning (-2 pts), but a function parameter typed as any poisons the entire execution scope and triggers an error (-5 pts).
  4. Native SARIF & CI Reports: Outputs GitHub Step Summaries and standard OASIS SARIF files for GitHub Code Scanning without third-party plugins.

⚡ Quick Start

Generate a .anyhunterrc.json configuration file:

npx any-hunter --init
Enter fullscreen mode Exit fullscreen mode

Run an audit against your tsconfig.json:

npx any-hunter ./tsconfig.json --threshold=85
Enter fullscreen mode Exit fullscreen mode

🧠 Behind the Scenes: Deep AST Inspection

Any-Hunter traverses the Abstract Syntax Tree (AST) using TypeScript's Compiler API to detect patterns such as:

  • Poisoned Parameters: function processData(payload: any)
  • Leaky Returns: const getUser = (): any => { ... }
  • Disguised Generics: Promise<any>, Array<any>, Record<string, any>
  • Compiler Bypasses: // @ts-ignore or // @ts-nocheck

When justifiable exceptions occur, developers can skip a line locally:

// any-hunter-disable-next-line
const payload = JSON.parse(response) as any;
Enter fullscreen mode Exit fullscreen mode

🤖 GitHub Actions Workflow

Integrate Any-Hunter directly into your Pull Request pipeline:

name: Type Health Check

on: [push, pull_request]

jobs:
  audit-types:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: JacobPalomo/any-hunter@v1.1.2
        with:
          threshold: 85
          exclude: '**/*.test.ts'
Enter fullscreen mode Exit fullscreen mode

🤝 Open Source

Any-Hunter is 100% open source under the MIT License.

Let me know in the comments what custom rules or frameworks you would like to see supported next!

Top comments (0)