Skip to main content

The State of TypeScript Tooling in 2026

·PkgPulse Team
0

TL;DR

TypeScript won. Now it's about how fast you can run it. TypeScript adoption crossed 75% of active npm packages in 2025. The 2026 tooling story is about speed: tsc is slow, and the ecosystem replaced it for everything except type checking. esbuild and SWC transpile TS in milliseconds. Biome (~2M downloads) replaced ESLint + Prettier for many teams. tsx (~4M downloads) runs TypeScript files directly without config. The focus shifted from "should I use TypeScript" to "how do I make my TS workflow fast."

Key Takeaways

  • TypeScript: ~50M weekly downloads — 75%+ npm package adoption, de facto standard
  • tsx: ~4M downloads — run .ts files directly, replaces ts-node for most uses
  • ts-node: ~8M downloads — original TS runner, slower than tsx/esbuild
  • Biome: ~2M downloads — Rust linter + formatter, replaces ESLint + Prettier
  • tsc — used ONLY for type checking (--noEmit); transpilation outsourced to esbuild/SWC

TypeScript Adoption Data

npm packages shipping TypeScript types (2026):
- Built-in types (.d.ts in package): ~55% of top 1000 packages
- DefinitelyTyped (@types/): ~20% covered
- No types: ~25% (mostly old/unmaintained packages)

New package creation with TypeScript:
- 2020: ~30% TypeScript
- 2022: ~55% TypeScript
- 2024: ~70% TypeScript
- 2026: ~82% TypeScript

TypeScript in project configs (Stack Overflow 2025 survey):
- 78% of professional JS developers use TypeScript
- Up from 69% in 2023

TypeScript 5.x to 6.0: What Changed in the Language

The TypeScript 5.x series introduced several language features that meaningfully affect how teams write and type their code, not just minor syntax conveniences. Understanding what changed in the language helps explain which tooling decisions now make sense.

TypeScript 5.0 brought const type parameters — a way to infer literal types from generic arguments without requiring as const at the call site. Before this, a function like createRoute('/users/:id') would infer the parameter as string rather than the literal '/users/:id'. With const type parameters, the inference happens automatically, which makes route builders, event emitters, and schema libraries significantly more precise. Hono's type-safe routing is built on exactly this feature.

The decorator overhaul in TypeScript 5.0 shipped the TC39 Stage 3 decorator proposal as the default, replacing the experimentalDecorators compiler option that NestJS and other frameworks had depended on for years. This matters for tooling because the new decorators behave differently from the legacy proposal: they don't require emitDecoratorMetadata, they work with esbuild and SWC (which never fully supported the metadata reflection approach), and they compose more predictably. Teams using NestJS or other decorator-heavy frameworks should verify their framework's compatibility with the Stage 3 decorators before migrating away from experimentalDecorators: true.

TypeScript 5.4 added the NoInfer utility type, which lets library authors prevent TypeScript from widening inferred types in specific positions. This is a tooling-adjacent feature because it improves the quality of type inference in configuration objects and builder patterns — the kind of code that framework-level libraries write.

TypeScript 6.0, released in 2025, made strictNullChecks mandatory and removed support for several long-deprecated flags (--noImplicitAny without --strict, --suppressExcessPropertyErrors, and others). The mandatory strict null checking is the most significant breaking change: any codebase that was not using "strict": true in tsconfig needs a migration pass before upgrading to TypeScript 6.0. For greenfield projects starting today, none of this is an obstacle — modern tsconfig baselines already use "strict": true.

TypeScript 6.0 also improved the performance of type checking through internal architecture changes, independent of the tsgo rewrite. Complex conditional types and mapped types that previously caused the language server to stall now resolve noticeably faster in the standard tsc compiler. The practical effect: VS Code becomes more responsive in heavily-typed codebases without any tooling changes on your part.

The compiler option changes in 5.x and 6.0 influence your tsconfig strategy. The "moduleResolution": "Bundler" option introduced in TypeScript 5.0 is now the recommended setting for projects using Vite, esbuild, or any bundler that handles module resolution — it removes the requirement to write .js extensions on relative imports in TypeScript source files while still producing correct output. For Node.js-native ESM projects without a bundler, "moduleResolution": "NodeNext" remains the right choice.


The tsgo (TypeScript Go Compiler) Story

In early 2025, Microsoft announced tsgo: a from-scratch reimplementation of the TypeScript compiler written in Go. This was not a transpiler-only project like esbuild or SWC — it was a full re-implementation of the TypeScript type checker itself, targeting the same semantic correctness as the original tsc.

The motivation was performance. The original TypeScript compiler is written in TypeScript, which means it runs on V8. Large TypeScript codebases — we are talking about projects with 500,000+ lines of TypeScript — experience type checking times measured in minutes, not seconds. Incremental builds help, but the fundamental ceiling of what TypeScript-running-on-V8 can achieve is lower than what a systems-language implementation can deliver. Microsoft's internal testing on projects like VS Code showed 10x faster type checking with tsgo compared to tsc.

As of early 2026, tsgo is available as a preview release and is being used internally at Microsoft. It is not yet at parity with tsc for all edge cases — certain complex conditional type scenarios and some obscure language features have known gaps. The tsgo team publishes a compatibility tracking document showing which TypeScript features are fully supported, partially supported, and not yet implemented.

The practical question for most teams: should you care about tsgo yet? Probably not for production use, but you should understand its trajectory. If your codebase has fewer than 100,000 lines of TypeScript, tsc with incremental builds is fast enough that tsgo's 10x improvement does not materially change your development experience. If you are working on a large monorepo where tsc --noEmit takes 3 minutes in CI, tsgo reaching production-ready status in 2026 or 2027 will be significant.

The more immediate implication is architectural: tsgo signals that the future of TypeScript type checking is a standalone binary with a language server protocol implementation, not a Node.js process. This aligns the TypeScript checker more closely with how Rust-based tools like rust-analyzer work — fast startup, native process, efficient memory usage. Editor integrations will be able to use tsgo as a drop-in replacement for the TypeScript language server once it reaches stability.

For tooling choices today: esbuild and SWC remain the right answer for transpilation (they do not type-check, so tsgo doesn't affect them). tsx remains the right answer for running TypeScript scripts directly. tsgo is relevant to the type-checking step — the tsc --noEmit step in your CI pipeline — and it will become relevant to your editor's TypeScript language server. Watch the tsgo GitHub for stability announcements. See also the tsx vs ts-node vs Bun comparison for deeper coverage of the execution layer.


Running TypeScript in 2026

tsx (The Modern ts-node)

# tsx — run TypeScript files directly (no config needed)
npm install -g tsx

# Run a TypeScript file
tsx scripts/seed-db.ts
tsx src/cli.ts --arg value

# Watch mode (like nodemon for TS)
tsx watch src/server.ts

# Environment variable (used in package.json)
# "scripts": { "dev": "tsx watch src/index.ts" }
// tsx — no tsconfig needed for basic use
// scripts/seed.ts
import { db } from '../src/db';
import { users } from '../src/schema';

await db.insert(users).values([
  { name: 'Alice', email: 'alice@example.com' },
  { name: 'Bob', email: 'bob@example.com' },
]);

console.log('✓ Database seeded');
process.exit(0);

// Run: tsx scripts/seed.ts
// No compilation step, no ts-node config, no esm flags
// package.json — common tsx patterns
{
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "start": "node dist/index.js",  // Production: still compile first
    "build": "tsc --noEmit && tsc -p tsconfig.build.json",
    "seed": "tsx scripts/seed.ts",
    "migrate": "tsx scripts/migrate.ts"
  }
}

tsx became the standard for running TypeScript scripts and development servers because it eliminates nearly all of the friction that made ts-node painful to set up. Under the hood, tsx uses esbuild for transpilation — no type checking, just a fast AST transformation from TypeScript to JavaScript. This makes it start in milliseconds rather than the seconds that ts-node required for loading the TypeScript compiler.

The ESM handling is where tsx's advantage over ts-node becomes most concrete. tsx transparently handles projects using "type": "module", CJS projects, and mixed projects without requiring flags or tsconfig changes. ts-node requires explicit --esm flags, specific tsconfig settings, and sometimes still fails with confusing errors when packages in your dependency tree use ESM. tsx's esbuild foundation handles the module format transformation as part of its normal operation, so you rarely think about it.

For watch mode — running a development server that restarts on file changes — tsx watch is a clean replacement for the nodemon-plus-ts-node combination that was common in 2020-2022. One tool, one command, native TypeScript support, fast restarts. Benchmarks on typical Node.js servers show tsx watch restart times of 200-400ms versus 1-2 seconds for ts-node with nodemon.

ts-node (Legacy but Still Used)

# ts-node — original TypeScript runner, slower
npm install -g ts-node

# ESM mode (needed for modern packages)
ts-node --esm src/index.ts

# With tsconfig
ts-node -P tsconfig.scripts.ts scripts/seed.ts

# Common issue: CJS vs ESM conflicts
# tsx handles this automatically; ts-node requires flags

Why tsx over ts-node: tsx uses esbuild under the hood (no type checking, just transforms). ts-node can optionally type-check but is 5-10x slower. For scripts and dev servers, tsx is the clear choice.

ts-node's 8M weekly downloads largely reflect existing projects and tools that were built before tsx existed. Packages that ship CLI tools with ts-node as a runtime dependency continue to pull those downloads. For new projects, there is no meaningful argument for choosing ts-node over tsx unless you specifically need the optional type checking that ts-node provides at runtime — a use case that is better served by separating execution (tsx) from type checking (tsc --noEmit in CI).


Type Checking Workflow

# The 2026 TypeScript workflow:
# 1. ts-node/tsx for execution (fast, no type checking)
# 2. tsc --noEmit for type checking (separate step)

# tsconfig.json — type checking config
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",           // For Node.js ESM
    "moduleResolution": "NodeNext",
    "strict": true,                 // All strict checks enabled
    "noUncheckedIndexedAccess": true, // arr[0] is T | undefined
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "skipLibCheck": true,           // Don't check .d.ts files (faster)
    "noEmit": true                  // Type check only, don't output
  }
}

# CI pipeline
npm run build       # Uses esbuild/tsup to transpile
npx tsc --noEmit    # Type check only — catches type errors
# tsup — build tool for publishing libraries
npx tsup src/index.ts --format cjs,esm --dts --clean

# Outputs:
# dist/index.cjs       — CommonJS
# dist/index.js        — ESM
# dist/index.d.ts      — TypeScript declarations
# dist/index.d.cts     — CTS declarations (dual-build)

# package.json exports field
{
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    }
  }
}

Project Structure and tsconfig Patterns

Real-world TypeScript projects rarely use a single tsconfig.json. Understanding the layering patterns that professional teams use prevents common configuration errors and significantly improves both build times and IDE experience.

The baseline pattern is a root tsconfig.json that defines shared compiler options but doesn't actually include any source files directly — its purpose is inheritance. Alongside it, you have tsconfig.build.json for the production compilation (includes source, excludes tests, emits output), and tsconfig.test.json for running tests (may have looser settings, may include test-only type declarations).

tsconfig.json          — base settings, no files, no emit
tsconfig.build.json    — extends base, includes src/, emits to dist/
tsconfig.test.json     — extends base, includes src/ + tests/

In monorepos, this pattern scales via TypeScript project references. Each package in the monorepo has its own tsconfig that references other packages it depends on. When you run tsc --build from the root, TypeScript only recompiles packages whose source files changed — the tsbuildinfo files store the compilation metadata needed for this incremental analysis.

Path aliases deserve careful treatment. The paths compiler option in tsconfig tells the TypeScript compiler how to resolve @/components/Button style imports, but it does not configure the actual module resolution at runtime or in bundlers. You must also configure path aliases in your bundler (Vite, webpack, esbuild) and in your test runner (Vitest, Jest). Failing to keep these in sync causes the frustrating situation where tsc reports no errors but the application fails at runtime. A practical rule: add paths to tsconfig first, then check that your bundler config mirrors it.

The skipLibCheck: true tradeoff is frequently misunderstood. It tells tsc to skip type checking of declaration files (.d.ts files) in node_modules. The primary benefit is speed — checking all declaration files in a large project can add seconds to type check time. The risk is that type errors in third-party packages are silently ignored. For most application codebases, skipLibCheck: true is the right default: you cannot fix third-party type errors anyway, and the speed improvement is real. For library authors publishing types themselves, skipLibCheck: false catches issues that users of your library would otherwise hit.

The strict mode flags that matter most in practice: noUncheckedIndexedAccess (makes array indexing return T | undefined instead of T, catching off-by-one errors that the base strict flag misses) and exactOptionalPropertyTypes (distinguishes between { key?: string } and { key?: string | undefined }, which catches a class of bugs in code that checks for property existence). Neither of these is included in the base strict: true preset — you must opt into them explicitly.


Biome (ESLint + Prettier Replacement)

// biome.json — replace both ESLint and Prettier
{
  "$schema": "https://biomejs.dev/schemas/1.5.0/schema.json",
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "correctness": {
        "noUnusedVariables": "warn",
        "noUnusedImports": "warn"
      },
      "style": {
        "useConst": "error",
        "noVar": "error"
      },
      "suspicious": {
        "noConsoleLog": "warn"
      }
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "trailingCommas": "all",
      "semicolons": "always"
    }
  },
  "files": {
    "ignore": ["node_modules", "dist", ".next", "build"]
  }
}
# Biome commands
npx biome check src/        # Lint + format check
npx biome format src/       # Format only
npx biome lint src/         # Lint only
npx biome check --write src/ # Fix auto-fixable issues

# Speed comparison on 500 TypeScript files:
# Biome:   ~80ms
# ESLint:  ~8,000ms
# Prettier: ~1,500ms
# ESLint + Prettier: ~9,500ms

Why teams switch to Biome: One config file, 100x faster than ESLint+Prettier combined, zero npm dependency hell. Trade-off: fewer rules than ESLint (especially for framework-specific rules like eslint-plugin-react-hooks).

The Biome adoption story is closely tied to the Biome vs ESLint debate that has been running in the TypeScript community since Biome v1 launched in 2023. The core argument for Biome is compelling: ESLint's plugin ecosystem grew organically over a decade and accumulated significant complexity. Running ESLint on a large TypeScript project requires configuring the TypeScript parser, reconciling ESLint's module resolution with TypeScript's, managing plugin version conflicts, and running Prettier as a separate process. Biome replaces this entire stack with a single binary, a single configuration file, and a consistent interface.

The argument against Biome is also clear: framework-specific lint rules don't exist yet. Teams using React, Vue, Next.js, or other frameworks rely on ESLint plugins (eslint-plugin-react-hooks, @next/eslint-plugin-next) for rules that catch real bugs. Until Biome has equivalent implementations, these teams must either keep ESLint for framework rules or accept the gap. The most common hybrid pattern: Biome for formatting and language rules, ESLint with only framework-specific plugins for React/Next.js rules.


TypeScript Config Patterns

// tsconfig.json — 2026 opinionated baseline
{
  "compilerOptions": {
    // Modern targets
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],

    // Module system
    "module": "ESNext",
    "moduleResolution": "Bundler",  // Best for Vite/esbuild

    // Strict mode (all of these, please)
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "exactOptionalPropertyTypes": true,

    // Quality of life
    "skipLibCheck": true,
    "noEmit": true,        // Let bundler emit
    "paths": {
      "@/*": ["./src/*"]   // Path aliases
    }
  },
  "include": ["src", "tests"],
  "exclude": ["node_modules", "dist"]
}

TypeScript at Scale: What Large Teams Do Differently

Teams maintaining codebases with 100,000+ lines of TypeScript develop habits around tooling that smaller teams don't need — but understanding these patterns is useful for anticipating what you'll need as a project grows.

The most impactful optimization for large TypeScript codebases is project references, TypeScript's built-in monorepo support. Rather than treating the entire codebase as one compilation unit, project references let you declare explicit dependency boundaries between packages. TypeScript tracks which packages are up to date using .tsbuildinfo files and skips recompiling packages that haven't changed. On a 200,000-line monorepo, this can reduce CI type check time from 8 minutes to under 90 seconds for a single-package change.

The tsbuildinfo file deserves explicit attention in your CI configuration. These files are generated by tsc --build and contain a snapshot of the previous compilation's file hashes and type information. If your CI environment doesn't cache these files between runs, every CI run starts from scratch and you lose all incremental build benefit. Cache the .tsbuildinfo files alongside node_modules — they are typically small (a few MB even for large projects) and provide disproportionate speed improvement.

Large teams typically separate their type-check CI job from their transpile/test CI jobs. Type checking with tsc is CPU-intensive and scales with codebase size. Transpilation with esbuild or SWC is fast and does not type-check. Running them in parallel — tsc for type correctness, esbuild for building the test bundle — keeps CI feedback loops short. Type errors are caught in the CI job that runs specifically for type checking, not in the test runner that already finished in 30 seconds.

Path aliases require synchronization across tools, but at scale this synchronization must be automated rather than manual. Teams typically maintain a single source of truth for path aliases — usually in tsconfig.json — and use scripts or build tool configurations that read from it. A mismatch between tsconfig paths and bundler aliases causes the class of bugs where code works in development (where the bundler resolves paths) but fails in type checking or in production builds that use a different tool.

For editor performance, the VS Code TypeScript language server benefits from project references at scale. Without project references, opening a file in a large monorepo causes the language server to analyze the entire codebase as one unit. With project references, the language server can scope its analysis to the current package and its declared dependencies, keeping memory usage and response times reasonable. Teams maintaining large TypeScript codebases without project references often report VS Code becoming sluggish or unresponsive — this is usually the fix.


The Oxc and tsgo Future

The TypeScript tooling stack in 2026 is in the middle of a transition toward native-speed implementations that will complete over the next one to two years. Understanding where each layer is heading helps you make tooling decisions that won't need to be reversed.

Oxc is a JavaScript/TypeScript toolchain written in Rust, maintained by the Rolldown team. Its components include a parser, a linter, a transformer (Oxc Transform, analogous to Babel or SWC for TS-to-JS transformation), and a resolver. As of early 2026, Oxc's linter has shipped and is production-ready — it runs significantly faster than ESLint on large codebases. The Oxc transformer is in active development and is being used as the foundation for the Rolldown bundler's TypeScript handling.

The fully Rust/Go TypeScript pipeline looks like this: Oxc or SWC for transpilation (TypeScript to JavaScript, no type checking), tsgo for type checking (the Go reimplementation of tsc), Rolldown for bundling, and Biome for linting and formatting. This pipeline does not exist as a coherent product today — you would need to wire together multiple tools manually — but the individual components are at various stages of production readiness.

The practical timeline: Biome for linting and formatting is production-ready today. SWC for transpilation is production-ready (it's what Next.js uses). esbuild for bundling is production-ready and widely used. tsgo for type checking is in preview, likely production-ready for most codebases by late 2026 or 2027. Rolldown is in active development and may be ready for production use in Vite projects by late 2026. Oxc Transform is in development and will likely power the next generation of bundlers.

For practical tooling decisions today: use tsx (which uses esbuild) for development execution, tsc --noEmit for type checking, esbuild or tsup for production builds, and Biome for linting/formatting. When tsgo reaches stability, swapping the type-checking step from tsc to tsgo will require minimal changes to your CI configuration. The rest of the stack is unlikely to need changes. See the Biome vs ESLint comparison for package health metrics on the linting layer specifically.


The TypeScript Toolchain in 2026

Development:
  tsx watch server.ts          ← Run TypeScript directly

Type Checking:
  tsc --noEmit                 ← Only for types, no output

Library Publishing:
  tsup / unbuild               ← CJS + ESM + .d.ts in one command

Application Bundling:
  Vite / Turbopack / esbuild  ← Transpile + bundle (no type check)

Linting + Formatting:
  Biome (all-in-one)
  OR ESLint + Prettier (more rules but 100x slower)

Testing:
  Vitest                       ← Native TypeScript support via Vite

When to Choose Each Tool

ToolUse Case
tsxRun scripts, dev servers, one-off utilities
ts-nodeLegacy codebases, when tsx has compatibility issues
tscType checking only (--noEmit in CI)
tsupPublishing npm packages with CJS/ESM dual output
BiomeFast lint+format, TypeScript-heavy projects
ESLintWhen framework plugins are essential (React hooks, etc.)
esbuildBundling in CI, custom build pipelines

Compare TypeScript tooling package health on PkgPulse.

See also: tsx vs ts-node vs Bun: Running TypeScript Directly 2026, Biome vs ESLint + Prettier: The Linter Wars 2026, and the ESM migration guide for handling module format in TypeScript projects.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.