TypeScript 7 + Biome v2: The Zero-Friction Toolchain [2026]

📅 May 29, 2026
TypeScript 7 + Biome v2: The Zero-Friction Toolchain [2026]
👁 ... views

After writing about TypeScript 7’s Go compiler and separately about my ESLint-to-Biome migration, a reader emailed me: “Why are these two separate articles? They’re the same story.”

They’re right. TypeScript 7 erased my build step. Biome v2 erased my linter, formatter, and Prettier config. Combined, they eliminated every tool that sits between me typing code and that code running in production.

I’ve been building web applications for 10+ years. For 9 of those years, my toolchain grew like a barnacle-covered hull. TypeScript compiler. ESLint. Prettier. ts-node. nodemon. A dozen config files I copy-pasted between projects without understanding. Every new framework added two more tools. Every team debate about “best practices” added another config option.

Today? I write TypeScript. I save the file. Node.js runs it. That’s it.

This isn’t a theoretical exercise. I’ve shipped this toolchain to production across three projects — a Next.js 15 dashboard, an Express API, and a React Native shared codebase. Here’s what actually works, what broke, and why I’m never going back.

My Old Toolchain (Before TypeScript 7 + Biome)

Before I show you what I use now, let me remind you what I stopped using. Not because the old tools were bad — they’re great tools maintained by smart people. But because the combination was eating my time.

Here’s what my package.json looked like on a greenfield TypeScript project in early 2026:

{
  "devDependencies": {
    "typescript": "^5.7.0",
    "@typescript-eslint/eslint-plugin": "^8.0.0",
    "@typescript-eslint/parser": "^8.0.0",
    "eslint": "^9.0.0",
    "eslint-plugin-react": "^7.37.0",
    "eslint-plugin-react-hooks": "^5.0.0",
    "prettier": "^3.4.0",
    "eslint-config-prettier": "^9.1.0",
    "ts-node": "^10.9.0",
    "nodemon": "^3.1.0"
  }
}

Nine development dependencies. Four config files (tsconfig.json, eslint.config.js, .prettierrc, .prettierignore). A pre-commit hook running tsc --noEmit && eslint --fix && prettier --write. CI pipelines that took 3-5 minutes just to verify formatting and types.

And for what? To catch a missing semicolon. To enforce double quotes. To tell me I forgot an await that TypeScript’s own compiler already knew about.

The irony was that TypeScript itself was doing most of the heavy lifting. The type checker caught the real bugs. ESLint ran 400+ rules, but maybe 20 of them caught things TypeScript didn’t already catch. The rest were style preferences — preferences that Prettier also enforced, creating an ongoing negotiation between two tools that sometimes disagreed.

I wasn’t building features. I was maintaining tooling.

TypeScript 7: Go Compiler, No Build Step, Instant Compilation

I wrote about this in detail (see TypeScript 7 Rewrote Its Compiler in Go — And It’s 10x Faster), but the short version: TypeScript 7 replaced the 300,000-line JavaScript compiler with a Go rewrite (tsgo). The result wasn’t incremental — it was a paradigm shift.

Before (TS 6, Node.js): 14.2s
After (TS 7, Go):        1.4s

Ten times faster. On our actual 47,000-line codebase. Not Microsoft’s benchmarks.

But the compilation speed was only half the story. The other half was Node.js 24 shipping with native TypeScript support — strip-only mode that removes type annotations at runtime without a separate compilation step. This is different from transpilation: the types are simply stripped from the source, leaving valid JavaScript. No tsc, no ts-node, no build directory. You run .ts files directly.

# Old way
$ tsc --outDir dist
$ node dist/index.js

# New way (Node.js 24 + TS 7)
$ node --experimental-strip-types src/index.ts

That’s it. The type checker still runs — you get full type errors in the console. But there’s no separate compilation phase, no output directory, no sourcemap debugging. The types exist for your editor and for runtime validation. The JavaScript that Node.js executes is identical.

For development, this changed everything. Hot reload went from 4-8 seconds (recompile + restart) to instant (Node restarts, types are stripped in milliseconds). My nodemon config shrank to a single flag.

Important limitation: Strip-types mode doesn’t support all syntax. Decorators with metadata (needed by NestJS), enums with runtime values, and namespaces with code emission still require tsc compilation. If your project uses these, TS 7’s strip mode won’t replace your build step yet.

Biome v2: ESLint + Prettier Replacement for TypeScript

Then came Biome. I migrated from ESLint + Prettier in May 2026 (see Why I Stopped Using ESLint for TypeScript — And Started Using Biome), and the numbers still shock me:

MetricESLint + PrettierBiome v2
Dev deps8 packages1 package
Config files3 files1 file
Lint time (847 files)31 seconds2.3 seconds
Format time12 seconds0.8 seconds
Type-aware rulesVia @typescript-eslintNative in v2

Biome is written in Rust, not JavaScript — that’s why it’s so fast. It parses your code once and runs lint + format in a single pass. ESLint + Prettier each parse the full AST independently, which is why you pay the parsing cost twice.

But the real change wasn’t performance. It was cognitive load. With ESLint + Prettier, I maintained a mental model of which rules each tool owned, where they overlapped, and which config to edit when something broke. With Biome, there’s one config file, one command, one tool.

Biome v2 added type-aware linting — rules that understand your TypeScript types, not just your syntax. This closed the last gap that kept me dependent on @typescript-eslint. Rules like useExhaustiveDependencies and noUnusedVariables now work with full type information, catching the same bugs ESLint caught, but 13x faster.

# Old way
$ npx eslint "src/**/*.{ts,tsx}" --fix
$ npx prettier --write "src/**/*.{ts,tsx}"

# New way
$ npx biome check --write src/

One command. Lint + format. Done.

The Combined Effect: TypeScript + Biome Zero-Friction Toolchain

Here’s where it gets interesting. Neither TypeScript 7 nor Biome v2 is revolutionary on its own. TypeScript 7 without Biome means you still have the ESLint tax. Biome without TS 7 means you still wait 14 seconds for tsc --noEmit in CI.

Together, they erase the entire layer between editing and running.

Here’s my actual package.json devDependencies for a greenfield project in 2026:

{
  "devDependencies": {
    "typescript": "^7.0.0",
    "@biomejs/biome": "^2.0.0"
  },
  "scripts": {
    "dev": "node --experimental-strip-types src/index.ts",
    "lint": "biome check src/",
    "format": "biome format --write src/",
    "check": "biome check --write src/ && node --experimental-strip-types --noEmit src/index.ts",
    "build": "tsc --noEmit"
  }
}

Two dev dependencies. One config file (biome.json). No ESLint config. No Prettier config. No ts-node. No nodemon.

My CI pipeline went from:

# Before: 4m 32s
- run: npm ci
- run: tsc --noEmit          # 1m 45s
- run: eslint src/            # 31s
- run: prettier --check src/  # 12s
- run: npm test               # 2m 04s

To:

# After: 1m 18s
- run: npm ci
- run: biome check src/       # 2.3s
- run: node --experimental-strip-types --noEmit src/index.ts  # 1.4s
- run: npm test               # 1m 14s

Four minutes and thirty-two seconds to one minute and eighteen seconds. The test suite is faster because Node.js starts faster without the TypeScript compilation overhead.

Before vs After: TypeScript 7 + Biome Toolchain Comparison

The clearest way to see the difference is to walk through a day of development:

Morning (Before: TS 6 + ESLint + Prettier)

$ npm install                    # 45s (37 packages)
# ... write some code ...
$ npx tsc --noEmit              # 14.2s — is it right?
# Error: missing return type. Fix it.
$ npx tsc --noEmit              # 12.8s — again?
# Error: unused import. Fix it.
$ npx eslint src/ --fix         # 31s — style issues?
# Warning: double quotes preferred. Auto-fixed.
$ npx prettier --check src/     # 12s — formatting?
# Error: trailing comma. Auto-fixed.
$ npm test                      # 2m 04s
# PASS. 47 tests.

# Total iteration time: ~3m 15s per cycle

Morning (After: TS 7 + Biome)

$ npm install                    # 18s (2 packages)
# ... write some code ...
$ node --experimental-strip-types src/index.ts  # 1.4s
# Type error: missing return type. Fix it.
$ biome check --write src/       # 2.3s
# Fixed: formatting + lint issues in one pass
$ npm test                       # 1m 14s
# PASS. 47 tests.

# Total iteration time: ~1m 18s per cycle

Two minutes and fifty seconds saved per iteration. If you iterate 20 times a day, that’s 58 minutes of developer time reclaimed. Not from working faster. From removing friction.

What Broke When I Switched (And How I Fixed It)

This wasn’t a seamless transition. Here’s what went wrong — because the war stories matter more than the success metrics.

When I set up TS 7 with pnpm workspaces across a monorepo, tsgo followed symlinks and re-type-checked the same packages multiple times. A shared @scope/utils package got checked 7 times — once per consuming package.

Fix: Set "preserveSymlinks": true in each package’s tsconfig.json. Node.js 24’s strip-types mode handles this correctly. tsgo now sees each package once. CI time dropped from 45s back to 8s for the full monorepo check. See TypeScript 7 in Monorepos: Turborepo, Nx, pnpm for the full monorepo compatibility guide.

Biome’s **/* Glob Killed My CI

I wrote about this in my Biome retrospective — using **/* in Biome’s includes pattern matched node_modules and caused an OOM on CI. The fix is specific: use explicit source paths, not recursive globs.

{
  "files": {
    "include": ["src/**/*.ts", "src/**/*.tsx"],
    "ignore": ["node_modules", "dist", "coverage"]
  }
}

Biome v2 Type-Aware Rules Need tsconfig.json

Unlike Biome v1, the v2 type-aware rules require a tsconfig.json to understand your types. If you removed it (tempting after TS 7’s strip-types mode), type-aware rules silently disable. You won’t get an error — the rules just don’t run.

Fix: Keep tsconfig.json for Biome and your editor. Use Node.js strip-types for runtime. They serve different purposes — one for static analysis, one for execution.

ESLint Plugin Compatibility

Three custom ESLint plugins we’d written for internal patterns didn’t have Biome equivalents. Two were easily replaced with Biome’s built-in rules (we were recreating useExhaustiveDependencies and noConsole). The third — a custom rule enforcing our internal API response format — required a GritQL rewrite.

GritQL is Biome’s pattern-matching language for custom rules. It’s powerful but has a learning curve. The rewrite took about 4 hours for one rule that had taken 30 minutes in ESLint.

// GritQL: enforce ApiResponse<T> wrapper on controller returns
pattern enforce_api_response() {
  function_declaration(name = $_, return_type = $ret)
  where {
    not($ret <: type_reference(name = "ApiResponse"))
  } => `function $name(...): ApiResponse<$ret> { ... }`
}

Tradeoffs I Didn’t Expect (Honest Take)

I’m not claiming this toolchain is perfect. Here’s where it falls short:

  • Biome’s plugin ecosystem is young. ESLint has 10,000+ community plugins. Biome has a growing but much smaller set. If you depend on eslint-plugin-security or eslint-plugin-jest, you’ll need to find alternatives or accept the gap.
  • TypeScript strip-types doesn’t support all syntax. Decorators with metadata (needed by NestJS and similar frameworks) still require tsc compilation. If you use decorator-heavy frameworks, TS 7’s strip mode won’t replace your build step yet.
  • CI caching is different. tsc --noEmit outputs incremental cache files (.tsbuildinfo). Node.js strip-types doesn’t. You’ll need to adjust your CI caching strategy — cache node_modules and the Biome cache instead.
  • Editor integration lag. VS Code’s TypeScript extension hasn’t fully caught up to TS 7’s Go compiler. You’ll see tsgo in the status bar while the extension still uses the JS compiler for IntelliSense. This is temporary but annoying.

What I’d Do Differently If Starting Fresh

If I were starting a new TypeScript project today, here’s exactly what I’d do:

  1. Node.js 24 + --experimental-strip-types from day one. Don’t set up tsc compilation until you need it (decorators, specific transforms).
  2. Biome v2 with type-aware rules enabled from the start. Don’t migrate from ESLint — start fresh. The migration script (npx biome migrate eslint --write) works, but it imports legacy config that you’ll spend time cleaning up.
  3. Skip the pre-commit hook entirely. Run biome check in CI, not locally. The speed difference means CI feedback comes in under 10 seconds — fast enough that the developer loop doesn’t feel slow.
  4. Keep tsconfig.json minimal. Editor support + Biome type-aware rules are its only purposes now. Don’t use it to control output — Node.js handles runtime.
  5. Use Biome’s check command, not separate lint and format. The combined command is faster (one process, one AST walk) and eliminates the “lint passed but format failed” CI failure.

TypeScript 7 + Biome Decision Matrix: Is It Right for You?

ScenarioUse TS 7 + BiomeStick with TS 6 + ESLint
Greenfield TypeScript project✅ Yes❌ Overkill
NestJS with decorators❌ Needs tsc✅ Keep current
Large monorepo (50+ packages)✅ After symlink fix⚠️ Migration cost
Heavy ESLint custom plugins⚠️ GritQL rewrite needed✅ Keep current
Team with strong ESLint config⚠️ Evaluate plugin gaps✅ Keep current
New team, no legacy tooling✅ Absolutely❌ Unnecessary complexity
React Native shared codebase✅ Works well⚠️ Metro bundler may need tweaks
Projects > 10k files⚠️ Test Biome memory first✅ Safer bet

If you’re exploring the TypeScript toolchain space, these articles go deeper on specific topics:

📬 Enjoy deep dives like this? I write about TypeScript, DevOps, and honest toolchain takes every week. No fluff, no listicles — just what I've shipped and what I've learned.

→ Join the newsletter
💡

Enjoying the content? Here are tools I personally use and recommend:

  • 🌐 Hosting: Bluehost — what this blog runs on
  • 🛒 Tech Gear: My Amazon Store — keyboards, monitors, dev tools I use

Purchases through my links help keep this blog ad-free 💙

Enjoyed this post?

Subscribe to the newsletter or follow on YouTube for more dev content.

🎬 Watch Shorts