TypeScript’s type system catches a meaningful class of bugs before code runs, type mismatches, missing properties, incorrect function signatures. What it cannot catch is everything else: security vulnerabilities that depend on how data flows through the application, architectural violations that accumulate over time as team boundaries blur, dead exports that remain in the codebase long after their callers were removed, and async programming mistakes that compile correctly but fail under specific runtime conditions. Static analysis tools fill these gaps, and choosing the right combination depends on what you are actually trying to find.
This guide covers the tools that matter for TypeScript teams in 2026: the linting layer (ESLint with typescript-eslint, Biome, OxcLint), the security layer (Semgrep, Snyk Code, SonarQube), the architectural layer (Dependency-Cruiser, Deptrac, Nx), the dead code layer (Knip), and the deep analysis layer (ts-morph, the TypeScript Compiler itself). For each, the focus is on what it actually does, how to configure it, and when it is not the right tool for the job.
Built for Codebases Nobody Understands
SMART TS XL surfaces breaking changes across your entire codebase automatically.
Learn MORETypeScript Static Analysis Tools: Comparison Table
Before diving into individual tools, the table below maps each tool to its primary function, CI suitability, and ideal use case. No single tool covers all dimensions, effective TypeScript quality analysis requires layering several.
| Tool | Primary Function | CI Suitable | Cost | Best For |
|---|---|---|---|---|
| ESLint + typescript-eslint | Linting, style, type-aware rules | Yes | Free | Team-wide conventions, async type safety |
| Biome | Linting + formatting | Yes | Free | ESLint + Prettier replacement, speed |
| OxcLint | Linting (ESLint-compatible) | Yes | Free | Monorepos needing fast lint times |
| TypeScript Compiler (tsc) | Type checking | Yes | Free | Type errors, strict mode enforcement |
| Semgrep | SAST, custom patterns | Yes | Free + paid | Security scanning, custom org rules |
| Snyk Code | SAST, dependency security | Yes | Free + paid | Security-first teams, IDE integration |
| SonarQube / SonarCloud | Quality gates, trend tracking | Yes | Free + paid | Enterprise quality dashboards |
| SonarLint | IDE-level quality feedback | IDE only | Free | Inline security and quality hints |
| Dependency-Cruiser | Dependency graph enforcement | Yes | Free | Architectural rule validation |
| Deptrac | Layer boundary enforcement | Yes | Free | Clean architecture, DDD boundaries |
| Nx | Monorepo dependency management | Yes | Free + paid | Monorepo module boundaries |
| Knip | Dead code and unused exports | Yes | Free | Reducing unused code at scale |
| ts-morph | Programmatic TS AST analysis | Selective | Free | Custom analysis, codemods, tooling |
Layer 1: Linting and Style
ESLint with typescript-eslint
ESLint remains the foundation of TypeScript linting. With the typescript-eslint package, it gains access to TypeScript’s type information and can enforce rules that require type context, the most valuable of which are the async-specific rules that catch common Promise mistakes.
bash
npm install --save-dev typescript-eslint
javascript
// eslint.config.js
import tseslint from "typescript-eslint";
export default tseslint.config(
...tseslint.configs.strictTypeChecked,
{
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
// Async safety rules -- highest value TypeScript-specific rules
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/require-await": "warn",
// Type safety
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-return": "error",
},
}
);
The four async rules above are the highest-value addition typescript-eslint provides over plain ESLint. They catch: unhandled Promises (floating promises), await applied to non-Promise values, Promises passed to callbacks expecting synchronous functions, and async functions that never use await. These are patterns that compile cleanly but produce runtime failures under specific conditions.
What ESLint cannot do: inter-file data flow analysis, architectural boundary enforcement, security taint tracking, or dead export detection. For those, the other layers below are required.
Biome: The Modern ESLint + Prettier Replacement
Biome replaces both ESLint and Prettier with a single Rust-based binary that runs 25-35x faster. It supports JavaScript, TypeScript, JSX, and JSON. For new projects or teams frustrated by ESLint’s plugin complexity and slow performance in large codebases, Biome is the strongest modern alternative.
bash
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
bash
# Check and format in one pass
npx @biomejs/biome check --write src/
# CI mode -- no modifications, exit non-zero on any finding
npx @biomejs/biome ci src/
Biome’s plugin ecosystem is smaller than ESLint’s, which matters for teams with extensive custom rules or framework-specific plugins. For teams using only the core ESLint rules and Prettier formatting, Biome provides equivalent coverage at a fraction of the execution time.
OxcLint: Speed-First ESLint Compatibility
OxcLint (part of the Oxc project) runs ESLint-compatible rules at 50-100x faster execution. It does not replace the full ESLint ecosystem but serves as the fastest option for CI pipelines where lint time is a bottleneck.
bash
npm install --save-dev oxlint
npx oxlint src/
OxcLint is the right choice for large monorepos where standard ESLint takes minutes and feedback loop latency is reducing pull request quality. It works best alongside ESLint rather than replacing it, run OxcLint for the fast feedback path and ESLint for full rule coverage in the pre-merge gate.
Layer 2: Type Checking, The TypeScript Compiler
The TypeScript compiler (tsc) is not just a build tool, it is the primary static analysis engine for type-level correctness. Enabling strict mode activates the settings that catch the most real-world bugs:
json
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"exactOptionalPropertyTypes": true,
"useUnknownInCatchVariables": true
}
}
bash
# Type-check only, no emit -- ideal for CI
npx tsc --noEmit
noUnusedLocals and noUnusedParameters at the compiler level catch unused variables and function parameters. useUnknownInCatchVariables (TypeScript 4.4+) types caught exceptions as unknown rather than any, forcing explicit type narrowing before use.
TypeScript control flow analysis is a built-in capability the compiler provides for narrowing types within conditional branches. It is not a separate tool, enabling strict mode ensures it is applied with full rigor. The compiler’s control flow analysis understands type guards, typeof checks, instanceof, and discriminated union patterns.
The tsc limitation: the TypeScript compiler does not find security vulnerabilities, architectural boundary violations, or dead exports. It finds type errors, and it does so definitively.
Layer 3: Security, SAST for TypeScript
Semgrep: Pattern-Based Security Scanning
Semgrep finds security vulnerabilities through code pattern matching. For TypeScript, it can detect SQL injection, XSS, hardcoded credentials, unsafe eval usage, prototype pollution, and insecure Express.js configurations, patterns that compile correctly but introduce exploitable vulnerabilities.
yaml
# Custom rule: flag user input in SQL queries (TypeScript)
rules:
- id: ts-sql-injection-risk
patterns:
- pattern: |
const query = `SELECT ... ${$USER_INPUT} ...`;
message: "User input directly interpolated into SQL -- use parameterized queries"
languages: [typescript]
severity: ERROR
bash
# Run with the community TypeScript security rules
semgrep scan --config=p/typescript --config=p/owasp-top-ten src/
Semgrep is complementary to ESLint, not competitive with it. ESLint enforces conventions; Semgrep finds security anti-patterns. Most TypeScript teams that care about security should run both.
Snyk Code: ML-Based SAST with IDE Integration
Snyk Code performs SAST with a machine-learning-based analysis engine that tracks taint flows across files. It integrates into VS Code and JetBrains IDEs, surfacing findings inline as developers write code rather than waiting for a CI run.
bash
npm install --save-dev snyk
npx snyk auth
npx snyk code test
Snyk Code’s developer-experience focus, inline IDE feedback, fix suggestions alongside each finding, and remediation examples, makes it the better choice when security education is as important as security detection.
SonarQube and SonarLint: Quality Gates and Trend Tracking
SonarQube provides continuous code quality analysis with a dashboard, trend tracking, and pull request decoration. For TypeScript it detects bugs, code smells, security hotspots, and duplications. SonarLint is the IDE extension that surfaces SonarQube rules locally.
For TypeScript teams, SonarCloud (the cloud-hosted version) is the simpler path: free for public repositories, with CI integration through GitHub Actions or GitLab CI in minutes.
yaml
# .github/workflows/sonar.yml
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SonarQube’s primary value over raw linting is its trend model: how is code quality evolving over time, which components are deteriorating, and what is the new-code quality gate score for each pull request. These management-facing metrics are what SonarQube does that ESLint and Semgrep cannot.
Layer 4: Architectural Analysis
Dependency-Cruiser: Enforce Module Boundary Rules
Dependency-Cruiser validates that your import graph follows defined architectural rules. It generates visual dependency graphs and fails CI when code violates module boundary constraints.
javascript
// .dependency-cruiser.cjs
module.exports = {
forbidden: [
{
name: "no-circular",
severity: "error",
comment: "Circular dependencies make code hard to test and maintain",
from: {},
to: { circular: true },
},
{
name: "no-ui-in-domain",
severity: "error",
comment: "Domain modules must not import from UI layer",
from: { path: "^src/domain" },
to: { path: "^src/ui" },
},
{
name: "no-external-in-shared",
severity: "warn",
comment: "Shared utilities should minimize external dependencies",
from: { path: "^src/shared" },
to: { pathNot: "^(src|node_modules/(lodash|date-fns))" },
},
],
};
bash
npx depcruise --validate .dependency-cruiser.cjs src/
Dependency-Cruiser directly addresses the “react dependency analysis cli tool” queries in the Search Console data, it is the standard tool for generating and validating dependency graphs in TypeScript/React projects.
Deptrac: Layer-Based Boundary Enforcement
Deptrac enforces architectural layers, ensuring that persistence code cannot import from presentation, that domain objects do not depend on infrastructure, and that module boundaries are respected across the entire codebase.
yaml
# deptrac.yaml
parameters:
layers:
- name: Domain
collectors:
- type: directory
value: src/domain
- name: Application
collectors:
- type: directory
value: src/application
- name: Infrastructure
collectors:
- type: directory
value: src/infrastructure
ruleset:
Domain:
- ~Infrastructure # Domain must not depend on Infrastructure
Application:
- Domain
Infrastructure:
- Application
- Domain
Deptrac is most valuable in projects following Clean Architecture, DDD, or Hexagonal Architecture patterns where layer isolation is a design constraint, not just a preference.
Nx: Monorepo-Level Dependency Management
For TypeScript monorepos, Nx provides module boundary enforcement, affected-build detection, and dependency graph visualization across all projects in the repository.
json
// .eslintrc.json -- Nx module boundary rules
{
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"allow": [],
"depConstraints": [
{ "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] },
{ "sourceTag": "scope:feature", "onlyDependOnLibsWithTags": ["scope:shared", "scope:feature"] },
{ "sourceTag": "scope:app", "onlyDependOnLibsWithTags": ["scope:shared", "scope:feature"] }
]
}
]
}
}
Nx’s affected commands run only the tests and linting relevant to changed modules, reducing CI time in large monorepos significantly.
Layer 5: Dead Code Detection
Knip: Find Unused Exports, Files, and Dependencies
Knip analyzes the full module graph to identify unused exports, unused files, and unused package.json dependencies, the class of code accumulation that ESLint’s no-unused-vars cannot catch because it only looks within a file.
bash
npm install --save-dev knip
npx knip
json
// knip.json
{
"entry": ["src/index.ts", "src/**/*.test.ts"],
"project": ["src/**/*.ts"],
"ignore": ["src/generated/**"],
"ignoreDependencies": ["vitest"]
}
Knip is directly searched in the SC data as “dead code detection unused exports javascript typescript.” It is the most capable current tool for module-level dead code identification in TypeScript projects.
Layer 6: Programmatic Analysis, ts-morph
ts-morph is a TypeScript compiler API wrapper that makes it straightforward to write custom analysis, codemods, and tooling against the TypeScript AST. It is searched directly in the SC data as “ts-morph” and “ts morph.”
typescript
import { Project } from "ts-morph";
const project = new Project({ tsConfigFilePath: "tsconfig.json" });
// Find all async functions that never await anything
const suspiciousAsyncFns: string[] = [];
for (const sourceFile of project.getSourceFiles()) {
for (const fn of sourceFile.getFunctions()) {
if (fn.isAsync()) {
const hasAwait = fn.getDescendantsOfKind(
SyntaxKind.AwaitExpression
).length > 0;
if (!hasAwait) {
suspiciousAsyncFns.push(
`${sourceFile.getFilePath()}:${fn.getName() ?? "anonymous"}`
);
}
}
}
}
console.log("Async functions with no await:", suspiciousAsyncFns);
ts-morph is not a ready-to-run analysis tool, it is the library you use to build one. It is appropriate for teams that need custom analysis beyond what existing tools support: migration scripts, custom architectural validators, automated refactoring, or code generation pipelines.
TypeScript Async/Await Static Analysis
The Search Console data shows a specific cluster of queries around “typescript static analysis async await paper tool” and “typescript static analysis async await vulnerability paper tool.” These reflect practitioners searching for tools that understand async programming errors at the type level.
The practical answer for production TypeScript teams is typescript-eslint’s four async-specific rules (no-floating-promises, await-thenable, no-misused-promises, require-await) combined with strictNullChecks and useUnknownInCatchVariables. Together, these catch the most common async type errors without requiring academic research tools.
For teams doing security research on async vulnerability patterns, TAJS and Jelly are academic static analyzers that model JavaScript’s async execution semantics, but they are research tools, not production development tools.
Angular and React: Framework-Specific Static Analysis
For Angular teams, @angular-eslint provides Angular-specific linting rules covering component patterns, template analysis, and service injection. It integrates with the ESLint + typescript-eslint configuration above.
For React teams, the eslint-plugin-react, eslint-plugin-react-hooks, and eslint-plugin-jsx-a11y plugins cover React-specific patterns. Dependency-Cruiser handles React-specific dependency graph analysis and is the tool behind the “react dependency analysis cli tool” queries in the SC data.
IDE Integration: Tools for VS Code and JetBrains
The queries “top code quality tools for VS Code integration” and “top code quality tools for IDE integration” reflect a real need: CI feedback arrives too late to change behavior. IDE-integrated analysis provides the tightest feedback loop.
VS Code: ESLint extension with rust-analyzer Clippy parity, SonarLint extension for inline SonarQube rules, Snyk extension for security findings, and the built-in TypeScript language service for type-level feedback.
JetBrains (WebStorm/IntelliJ): WebStorm ships with built-in TypeScript support that surfaces type errors inline, plus integration with ESLint, Prettier, and SonarLint. The built-in inspection system covers many of the same patterns as ESLint rules.
VS Code configuration for maximum TypeScript static analysis coverage:
json
// .vscode/settings.json
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["javascript", "typescript", "typescriptreact"],
"sonarlint.connectedMode.project": {
"connectionId": "my-sonarcloud",
"projectKey": "my-org_my-project"
}
}
How SMART TS XL Extends TypeScript Analysis Across the Enterprise
The tools above cover TypeScript within a TypeScript project. In organizations where TypeScript services interact with COBOL batch programs, Java APIs, Python data pipelines, or legacy mainframe systems, single-language tools cannot see the dependencies that cross language boundaries.
SMART TS XL’s static code analysis covers TypeScript alongside every other language in the enterprise environment, COBOL, JCL, Java, Python, RPG, SQL, and builds a unified dependency model across all of them. When a TypeScript service calls an API that is backed by a COBOL program, SMART TS XL can trace that relationship and include it in impact analysis before any change is made to either component.
The enterprise search capability makes the entire multi-language codebase queryable: find every TypeScript import of a specific module, every Java method that a TypeScript service calls, every COBOL copybook that feeds data to a TypeScript-consumed API, in seconds, across millions of lines of code in any combination of languages.
For enterprise teams that operate TypeScript as one language in a larger portfolio, SMART TS XL provides the architectural visibility and cross-language dependency mapping that makes TypeScript-specific tools look at their part of the system while SMART TS XL sees the whole.
Building the Right Stack
No single tool covers all quality dimensions for TypeScript. The right approach is layered, with each layer targeting a distinct class of problem:
Minimum viable stack (new project, small team):
- TypeScript strict mode
- ESLint with typescript-eslint async rules
npm auditfor dependency vulnerabilities
Production application stack (medium team, security-conscious):
- Everything above, plus:
- Biome or Prettier for formatting
- Semgrep for security SAST
- Knip for dead code
- SonarCloud for quality trend visibility
Enterprise / monorepo stack (large team, architectural constraints):
- Everything above, plus:
- Dependency-Cruiser for module boundary enforcement
- Nx for monorepo affected-build optimization
- Deptrac for layer boundary validation
- SonarQube (self-hosted) for on-premises quality gates
Polyglot enterprise stack (TypeScript alongside legacy systems):
- Everything above, plus:
- SMART TS XL for cross-language impact analysis and dependency mapping