Type-aware unused code checks for TypeScript
Type-aware analysis confirms with the TypeScript checker that a symbol is unused before you delete it, with evidence for symbol usage and affected tests.
Type-aware mode lets you delete or refactor TypeScript code with exact TypeScript checker evidence. It follows exact symbol identity across aliases, re-exports, packages, and tests.
The default fallow analysis is Rust-native and syntactic. It does not need Node.js or the TypeScript compiler. Add --type-aware when a cleanup or refactor depends on exact TypeScript symbol identity.
The optional companion adds these project-wide capabilities:
- Exact symbol-use confirmation for existing dead-code candidates.
- Symbol traces with namespaces, aliases, and re-export hops.
- Package API surfaces and private type leaks across files.
- Exact consumers, affected files, and targeted tests.
- Public-signature type coupling, as advisory findings.
Use this pass when a real cleanup or refactor decision depends on TypeScript identity. For fast everyday feedback, keep the normal syntactic run.
fallow type-aware status --format json --quiet
fallow dead-code --unused-class-members --type-aware --format json --quiet
fallow fix --type-aware --dry-run --format json --quiet
fallow dead-code --type-aware --symbol-impact src/api.ts:Client --format json --quiet
fallow health --type-aware --type-coupling --format json --quiet
What this does not replace
Keep tsc --noEmit for compiler correctness and Oxlint for local lint rules. Fallow does not emit TypeScript diagnostics or repeat local typed lint findings.
Type-aware fallow answers project-wide questions that those tools do not:
- Is this exact declaration used through an alias or re-export?
- Which production files and tests depend on this exported symbol?
- Does a public signature expose a package-private type?
- Which public types couple packages or modules together?
Conservative results
Every semantic result reports complete, partial, or unavailable. Fallow removes a dead-code candidate only when the checker resolves the exact identity of its declaration. When the analysis is partial or unavailable, fallow keeps the original finding and reports a stable omission reason.
fallow fix --type-aware offers a class-member removal only when two conditions are true: every owning project returns complete negative evidence, and the declaration guard still matches. Always preview with --dry-run.
Evidence that the checker can see does not prove that a published API has no external consumers. For library boundaries, package exports, publicPackages, visibility tags, and explicitly selected consumer projects stay authoritative.
To fail the CI job when semantic evidence is incomplete, use --type-aware-require complete:
fallow dead-code --type-aware --type-aware-require complete \
--format json --quiet
When automatic project selection misses a consumer, add a --type-aware-project flag for each project. The flag is repeatable:
fallow dead-code --type-aware \
--type-aware-project tsconfig.json \
--type-aware-project packages/web/tsconfig.json \
--format json --quiet
Svelte virtual-module exports
Svelte language tooling creates virtual TypeScript modules for .svelte files. TypeScript-Go does not yet expose the plugin integration that fallow needs to build the same virtual modules. Fallow still supports default component imports, and named exports backed by declarations that the checker can see.
A selected project can use a named import, named re-export, or wildcard re-export whose .svelte target does not resolve. In that case, fallow abstains with svelte-virtual-module-exports and reports the project and the affected semantic queries as unavailable. In best-effort mode, fallow keeps the syntactic result as advisory evidence. With --type-aware-require complete, the run fails.
For authoritative Svelte and TypeScript diagnostics, run svelte-check. Do not rewrite valid component exports only to satisfy fallow. Keep the Svelte project configuration as it is, and rely on the omission reason until TypeScript-Go provides a supported virtual-module host integration.
Persistent configuration
Keep the feature opt-in for each repository:
{
"typeAware": {
"enabled": true,
"projects": ["tsconfig.json"],
"require": "best-effort"
}
}
Start with best-effort. Use complete only when partial or unavailable semantic evidence must fail the run.
Performance
Type-aware analysis builds an additional TypeScript Program and scans checker symbols. On the current public release corpus, the added time is about 92 ms at the median and 2.3 seconds at P95. Large projects can take longer. Editor sessions reuse the loaded Program. One-shot CLI and CI runs pay the cold setup cost each time.
Installation
The fallow npm package installs an optional fallow-type-aware companion with a matching version. If you disable optional dependencies, install the exact same version manually. fallow type-aware status checks discovery, package version, protocol version, and backend version. It does not analyze the project.
Experimental tsrs backend
Set FALLOW_TYPE_AWARE_BACKEND=tsrs to run the semantic pass on tsrs, a Rust port of the TypeScript 7 checker, instead of the default TypeScript-Go backend. The environment variable is the only way to select the backend. There is no typeAware.backend config key, and the config file rejects it as an unknown field. The backend stays opt-in while tsrs is a 0.x release.
Fallow accepts any tsrs from 0.18.0 up to (not including) 1.0.0, so you can reuse the tsrs that your project already installs. It must be where fallow-type-aware can resolve it. To install the version that Fallow tests:
npm install --save-dev tsrs@0.20.1-ts7.1.0-beta
FALLOW_TYPE_AWARE_BACKEND=tsrs npx fallow type-aware status
Select the backend on each surface
Fallow reads the variable once per process. Every Fallow process that you start inherits it from its parent.
| Surface | How to select tsrs |
|---|---|
| CLI, npm scripts, and CI | Set FALLOW_TYPE_AWARE_BACKEND=tsrs in the script or in the job env. |
| MCP | Add the variable to the env block of the server entry in the MCP client config. |
| LSP in other editors | Set the variable in the environment of the fallow-lsp process. |
| VS Code | Start the editor with FALLOW_TYPE_AWARE_BACKEND=tsrs and with FALLOW_TYPE_AWARE_BIN set. See below. |
For MCP, use an env block like this:
{
"mcpServers": {
"fallow": {
"command": "fallow-mcp",
"env": { "FALLOW_TYPE_AWARE_BACKEND": "tsrs" }
}
}
}
The VS Code extension bundles a companion that holds only the default backend. That companion cannot load tsrs. With only the backend variable set, the type-aware pass is unavailable and Fallow shows conservative syntactic findings. To use tsrs in VS Code, set both variables and start the editor from that shell:
export FALLOW_TYPE_AWARE_BACKEND=tsrs
export FALLOW_TYPE_AWARE_BIN="$PWD/node_modules/.bin/fallow-type-aware"
code .
An existing FALLOW_TYPE_AWARE_BIN takes precedence over the bundled companion. Use an absolute path, because Fallow resolves a relative path from the working directory. The project must have tsrs installed where that companion can resolve it. The extension has no setting for the backend.
Check the selection
Run fallow type-aware status in the same environment as the surface. It names the backend and the version that ran. _meta.type_aware in the JSON output names them too. The command does not read the config file. A version outside the supported range makes the type-aware pass unavailable, and type-aware status names the range. An unknown backend value also makes the pass unavailable, and type-aware status names the known values.
Limits
- The tsrs backend refines findings, but its evidence never makes a fix eligible. Guarded fixes need the evidence of the default backend at the version that Fallow tests.
- tsrs
0.18.0has no Windows or macOS x64 binaries. Use0.20.0or later on those platforms. - The default backend also accepts any TypeScript 7 release that a package manager override selects.
- Fallow will consider a config key when tsrs reaches 1.0 or when the default backend moves to TypeScript 7.1 with corpus evidence for tsrs.