TypeScript 7 + Biome v2: The Zero-Friction Toolchain [2026]
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:
| Metric | ESLint + Prettier | Biome v2 |
|---|---|---|
| Dev deps | 8 packages | 1 package |
| Config files | 3 files | 1 file |
| Lint time (847 files) | 31 seconds | 2.3 seconds |
| Format time | 12 seconds | 0.8 seconds |
| Type-aware rules | Via @typescript-eslint | Native 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.
Monorepo Symlink Bugs in pnpm Workspaces
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-securityoreslint-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
tsccompilation. If you use decorator-heavy frameworks, TS 7’s strip mode won’t replace your build step yet. - CI caching is different.
tsc --noEmitoutputs incremental cache files (.tsbuildinfo). Node.js strip-types doesn’t. You’ll need to adjust your CI caching strategy — cachenode_modulesand 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
tsgoin 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:
- Node.js 24 +
--experimental-strip-typesfrom day one. Don’t set uptsccompilation until you need it (decorators, specific transforms). - 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. - Skip the pre-commit hook entirely. Run
biome checkin CI, not locally. The speed difference means CI feedback comes in under 10 seconds — fast enough that the developer loop doesn’t feel slow. - Keep
tsconfig.jsonminimal. Editor support + Biome type-aware rules are its only purposes now. Don’t use it to control output — Node.js handles runtime. - Use Biome’s
checkcommand, not separatelintandformat. 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?
| Scenario | Use TS 7 + Biome | Stick 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 |
Related Articles
If you’re exploring the TypeScript toolchain space, these articles go deeper on specific topics:
- TypeScript 5 Features You Should Know — const type params, using declarations
- TypeScript 6 Erased My Build Step — the original no-build-step paradigm
- TypeScript 7 Rewrote Its Compiler in Go — 10x speedup benchmarks
- TypeScript 7 in Monorepos: Turborepo, Nx, pnpm — monorepo compatibility guide
- Why I Stopped Using ESLint — And Started Using Biome — the original migration guide
- What 3 Months of Biome in Production Taught Me — CI OOM, GritQL, lessons 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