How to Find Unused Code in Your TypeScript Project Fast

Dan Greer · · 9 min read
Dashboard showing tools and metrics to find unused code in TypeScript project efficiently

It's all too common to find unused code in TypeScript projects, especially when new features and AI-powered tools generate more files than we realize.

That clutter can slow you down and introduce bugs you never saw coming.

We know it's frustrating to untangle dead code, so we've built a guide with:

  • Practical ways to find unused code in TypeScript project environments, from small apps to ambitious repositories
  • Strategies for mapping code relationships and catching overlooked dependencies
  • Integration tips for aligning dead code detection with your AI-driven workflows and CI systems

Understand Why Dead Code Accumulates in TypeScript Projects

Every fast-moving TypeScript repo grows dead code. You see it happen: features get rewritten, experiments get dropped, or someone leaves an unused export "just in case." As your team ships new features or automates development with agents, dead code quietly piles up behind the scenes. This is more than technical clutter. It's blockers for clean architecture and clean agent handoffs.

Why does it happen? Let's break down the ways dead code sneaks in:

  • Rapid iteration means you move quickly, but old functions or modules stick around once features exit. Fast growth always leaves something behind.
  • Teams hesitate to delete code, worried they'll hit a hidden edge case. Fear of breaking unseen consumers is real.
  • AI coding assistants like Claude Code or Copilot make it worse. They often can't see the whole codebase and end up duplicating logic. When agents generate what already exists, you get twins that turn into zombies.
  • Abstractions can mask deadness. React.FC, for example, kept unused props alive for years by hiding unused warnings from TypeScript.
  • Orphan chains snowball. Remove the last caller of a util, and suddenly you've got abandoned modules plus a stale dependency or two in package.json.
Dead code is the silent drag that all fast-moving TypeScript projects collect. It blocks clarity, makes agent-powered development riskier, and forces you into cautious, manual cleanup.

How Modern Tools Help

Rescue starts with the right linters and static analysis. TypeScript ESLint replaced TSLint and is now the baseline for surfacing unused locals and enforcing patterns. If you're missing rule coverage, you're already behind.

Code graph tools, like ENRE-ts or call graph analyzers, visualize not just what exists but how it's connected. Spotting whole subgraphs never touched from any entry point is a game changer for cleanup discipline.

Monorepo entry points in project references cut ambiguity. They flush out which exports are actually used and prevent accidental hoarding.

If you haven't set up routine dead-code checks in CI, you're flying blind.

Diagram showing how to find unused code in TypeScript project with highlighted dead code sections

Uncover the Hidden Costs of Unused Code

Every line of unused code comes with a tax. TypeScript projects, especially those relying on AI agents, pay for dead code in almost every workflow stage.

What does dead code really cost you?

  • Slower builds and bigger bundles instantly sap dev speed. As code piles up, so does wasted CI time and install disk use.
  • Hidden dependencies increase risk. Orphans may hide subtle coupling. Refactor fear becomes real because old code covers the tracks.
  • New devs and agents get lost. The codebase shape stops matching reality. Agents spin up duplicate functions and lose trust, while humans waste time hunting through legacy files.
  • Orphaned dependencies pollute your package.json. Remove one old module, and you might see a domino cascade of now-dead npm deps.

Static security tools connect the dots: Skylos, for example, finds both dead code and hidden security vulnerabilities, showing that what doesn’t get used can bite you in unexpected ways.

You want technical debt to drop, not creep up as your AI stack gets smarter. Run regular dead-code sweeps, and watch onboarding, build times, and bundle sizes all improve.

Cleaning dead code is the fastest way to reclaim focus, shrink operational costs, and set a higher bar for agent-driven engineering—no AI system thrives in a junkyard.
Find unused code in TypeScript project with analysis tools highlighting obsolete functions and files

Compare the Manual and Automated Ways to Find Unused Code

Trying to spot dead code by hand is slow, error-prone, and frustrating. Modern TypeScript is too dynamic for grep or "Find All References" to be enough—especially with agents and architectural complexity.

Manual Tactics vs. Static Analysis

  • Manual searching works for simple, flat projects but misses what’s really used via dynamic imports, strings, or test-only entry points. Anything outside the happy path is invisible.
  • TypeScript itself only catches unused locals. It won’t warn about an exported util that’s never called anywhere, or a whole file that’s orphaned.
  • Automated static tools exist but deliver different levels of value:
    • Knip is the current favorite for dead code. You run npx knip, it flags unused files, exports, and dependencies, and you decide what to cut. It's fast, plugin-ready, and doesn’t need global install. Can visualize debt, supports monorepos, and helps you automate cleanup or CI gates.
    • ts-prune: Quick CLI to find unused exports. Script-friendly but may ping things used by convention, so it needs a review pass.
    • unimported: Finds files and npm packages never imported. Useful in bigger projects to highlight “deadweight,” though dynamic patterns can create noise.
    • (tsr is now archived; Knip is its stronger modern replacement.)

If you're agent-driven, manual is not an option. Automate or get left behind.

Automated static analysis isn’t perfect, but it’s 10x better than searching by hand and hoping you didn’t miss a ghost import.

Set Up and Run Knip for Rapid Dead Code Detection

You need speed and confidence. Knip gets you both. There’s no tedious config, just run npx knip from your repo root. Knip analyzes entry points, checks every import tree, and spits out lists:

  • Unused files and exports land in your face, so you know what’s really dead
  • Unneeded npm packages are flagged, highlighting where your install times and security risks balloon
  • You get IDE-friendly links to jump straight to the dead code culprits

Review everything before deleting, especially for dynamic imports, but Knip makes the first sweep fast and clear. Monorepo? Knip handles it out of the box. Pair Knip output with your code reviews or CI checks to catch new zombies before they hit main.

Knip even offers a --fix flag to automatically cut most dead exports. Just review changes—a few patterns need a human eye.

A dead code tool you can trust makes agent-driven development safer and faster. All your rapid prototyping and aggressive refactoring pay off when you know exactly what to remove and when it's safe to do it.

Explore Other Tools and Techniques for Finding Unused Code

Knip isn’t alone, but it’s the go-to. Still, for advanced teams, combine approaches to catch edge cases and maximize coverage.

Other options:

  • ts-prune: Perfect for scripting into builds to catch unused exports. Fast and direct, though not always context-aware.
  • unimported: Flags files and deps that never get imported, exposing deadweight.
  • IDE tools: VS Code’s “Find All References” still works for spot checks or tricky edge cases but only sees the surface.

For monorepos or advanced setups:
Use project references to lock down real entry points, then run tools in each workspace for total coverage.

Smart teams use multiple signals:

  • Look for agreement between tools before deleting.
  • If only one flags an export, sanity-check with a search or quick review.
  • Dynamic frameworks? Complement analysis with graph tooling or runtime instrumentation to cut false positives.
The right mix of static analysis and project practice clears a path to cleaner, faster, and more reliable TypeScript, even as your AI agents and product move at top speed.

Understand the Limits of Static Analysis and Grep

No single tool catches everything. Static analysis and text searches have real blind spots you can't ignore, especially when your TypeScript project relies on dynamic imports, registry patterns, or heavy use of agents.

Where Grep and Static Tools Fall Short

  • Dynamically imported modules slip past static graphs. Code splitting, plugin systems, and inversion-of-control all hide usage.
  • String-based references and reflection dodge both compiler and grep. If something is registered by name instead of import, it's invisible to basic tools.
  • Test-only utilities and setup scripts get missed. Removing them silently breaks tests or dev tools.
  • You see false positives. Tools might flag things as dead that your runtime setup wires in at launch.
"Statically dead" isn’t always "really dead." Fast-paced agent workflows amplify these risks if your detection isn’t granular and current.

Get Granular With Advanced Analysis

Combining multiple approaches closes gaps. Use call graph and AST-based analysis. Pair tools like ENRE-ts or TypeScript Call Graph with runtime verification in tests. If an export lights up in test coverage or is called dynamically, it's alive for your architecture. Avoid risky removes. If in doubt, run a search for string references or use runtime checks to confirm reachability.

Block uncertainty at the root. Build process discipline around code deletion. Don’t trust a single signal.

Shift From File Lists to Living Architecture

Unused code is a symptom; architectural drift is the disease. For AI-native teams, having a clear map of your project’s living architecture unlocks the biggest leap in quality and velocity.

Move Beyond Folder-Minded Cleanup

  • Treat your codebase like a graph of entities, endpoints, and flows, not just a pile of files.
  • See which modules, dependencies, and exports connect—trace what is truly reachable from entry points.
  • Architectural knowledge empowers both humans and AI agents to reason safely. No more guesswork or accidental breakage.

This is how you future-proof agent-driven development. When agents see your knowledge graph, they understand and respect structure. This stops duplication and builds trust.

Living architecture means confident refactoring. It shrinks blast radius and brings clarity to each deploy, review, and AI-suggested change.

Automate Ongoing Dead Code Detection and Codebase Hygiene

Manual sweeps aren't enough, especially as your repo scales and agents join your workflow. The answer? Automation. Build dead-code detection directly into your process, so every PR and every merge gets checked.

How to Level Up With Automation

  • Run Knip or equivalent tools in CI to block rot instantly. Set up fast builds that flag dead files and unused dependencies before they reach main.
  • Gate merges on fixing or triaging flagged items. You save time later by being strict now.
  • Attach reports to pull requests. Track why you keep or delete flagged code. Build trust and create a living audit trail.
  • Use IDE-friendly output for instant reviews. Review, merge, delete, or explain with context.

Keep a record. Over time, show the hard numbers—modules removed, bundle and build times improved, onboarding friction dropped.

The strongest teams don’t just clean up dead code, they automate discipline so new zombies never creep in.

Integrate Dead Code Analysis With AI-Driven Development

If you’re building with AI at your side, you need every tool working for you, never against you. Orphaned code weakens AI coding agents, leading to duplication, breakage, and lost trust.

At Pharaoh, we’ve solved this exact problem with our Neo4j knowledge graph approach. By parsing your repo with Tree-sitter, mapping every module, endpoint, and dependency, then powering agent inquiries via MCP, we let both you and your AI verify reachability and blast radius at any moment.

When AI agents can query live structure—what connects, what’s orphaned—they avoid twin functions and breakages. This makes agent suggestions safer. It guarantees your code stays clean as you ship fast.

Your team gets speed, safety, and total awareness. Solo developers or small squads can now refactor with confidence, backed by architectural proof.

Context-aware AI isn’t just a dream—it's standard workflow when your codebase is structured, queryable, and open to agents and humans alike.

Frequently Asked Questions About Finding Unused Code in TypeScript Projects

Let’s bust a few myths and answer what top developer teams keep asking us.

  • What tools actually catch everything? No single tool does. Use TypeScript ESLint for locals, Knip for exports and files, and graph-based solutions—like Pharaoh—for architectural reachability.
  • How do I avoid deleting code that’s dynamically used? Combine static tools with runtime checks, test coverage, and explicit code review.
  • Does cleaning up dead code help my builds? Every time. Smaller bundles, faster CI, and tighter onboarding.
  • How do advanced tools like Pharaoh compare? We solve the architectural map. No one-off scripts; you get system-wide reachability, dependency maps, and instant answers for both agents and humans.
  • Monorepo? Configure entry points clearly so tools don’t "lose" shared modules.
  • Should I review every automated result? Always review before you delete, especially exports flagged by just one tool.
Stack discipline and layered tooling for an unbeatable signal. Clean code, trusted agents, consistent refactoring—this is what proactive teams prioritize.

Become Confident and Proactive in Codebase Cleanup

Unused TypeScript code sabotages your velocity, clarity, and agent performance. When you approach dead code with system-level discipline and structured automation, you flip the process—from anxious cleanup to ongoing code confidence.

Start with reliable tools. Move to architectural mapping. Automate every check, in every branch.

You work faster. Agents grow more reliable. Your team ships cleaner, smarter, and safer.

Want to go next level? Check out how Pharaoh resets the bar for architectural intelligence and agent-centric development. Lead with clarity, stay ahead, and enjoy the payoff of a lean, living codebase that powers innovation.

← Back to blog