20 Static Analysis Tools Every TypeScript Team Needs

TypeScript Static Analysis Tools: The Complete Guide for Development Teams

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 MORE

TypeScript 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.

ToolPrimary FunctionCI SuitableCostBest For
ESLint + typescript-eslintLinting, style, type-aware rulesYesFreeTeam-wide conventions, async type safety
BiomeLinting + formattingYesFreeESLint + Prettier replacement, speed
OxcLintLinting (ESLint-compatible)YesFreeMonorepos needing fast lint times
TypeScript Compiler (tsc)Type checkingYesFreeType errors, strict mode enforcement
SemgrepSAST, custom patternsYesFree + paidSecurity scanning, custom org rules
Snyk CodeSAST, dependency securityYesFree + paidSecurity-first teams, IDE integration
SonarQube / SonarCloudQuality gates, trend trackingYesFree + paidEnterprise quality dashboards
SonarLintIDE-level quality feedbackIDE onlyFreeInline security and quality hints
Dependency-CruiserDependency graph enforcementYesFreeArchitectural rule validation
DeptracLayer boundary enforcementYesFreeClean architecture, DDD boundaries
NxMonorepo dependency managementYesFree + paidMonorepo module boundaries
KnipDead code and unused exportsYesFreeReducing unused code at scale
ts-morphProgrammatic TS AST analysisSelectiveFreeCustom 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 audit for 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