Migrating from rules_ts¶
rules_typescript is a fresh implementation, not a fork of rules_ts from aspect-build. This page covers the differences honestly — including where rules_ts is the better choice.
When to use which¶
Choose rules_ts (Aspect) if:
- You have an existing large codebase that can't adopt isolated declarations
- You need full tsc compatibility for every TypeScript edge case
- You need Windows support today
- You want a battle-tested, BCR-published ruleset used in production by many companies
- You're already invested in rules_js and the Aspect ecosystem
Choose rules_typescript (this) if:
- You use Vite for bundling and dev serving
- You want Gazelle to generate all BUILD files (zero manual maintenance)
- You want sub-second incremental rebuilds via the .d.ts compilation boundary
- You're starting a new project or can adopt isolated declarations
- You use Remix, TanStack Start, or other Vite-based frameworks
- You want zero system prerequisites (no Node, no pnpm install needed)
Comparison¶
| rules_ts (Aspect) | rules_typescript (this) | |
|---|---|---|
| Compiler | tsc (JavaScript) | Oxc (Rust) — 10-100x faster per file |
| Type-checker | tsc | tsgo (Go port of TypeScript) |
| Compilation boundary | tsc project references | .d.ts via isolated declarations |
| Bundler | Bring your own | Vite (first-class, built-in) |
| Dev server | None built-in | Vite with HMR + React Fast Refresh |
| npm management | rules_js (pnpm virtual store, symlinks) | Own pnpm lockfile parser (simpler) |
| BUILD generation | Aspect CLI (proprietary) | Gazelle (open-source, directives) |
| Framework support | None built-in | Remix, TanStack Start, SvelteKit, Solid Start |
| Dependencies | rules_js + rules_nodejs | rules_nodejs only |
| Isolated declarations | Not required | Required (or opt-out per package) |
| pnpm | System install required | Hermetic (bazel run //:pnpm) |
| BCR | Published, stable | Pre-release (v0.1.0) |
| Production users | Many companies | None yet |
| Windows | Supported | Partial (node_modules action only) |
Trade-offs: where rules_ts is better¶
Full tsc compatibility¶
tsc IS TypeScript. Every compiler flag, every tsconfig.json option, every edge case works exactly as documented. Our Oxc + tsgo stack covers the vast majority of TypeScript, but:
- Decorator metadata (
emitDecoratorMetadata) may behave differently in oxc - Very new TypeScript syntax may lag behind tsc by a few weeks until oxc implements it
- Some exotic
tsconfig.jsonoptions (e.g.,verbatimModuleSyntaxedge cases) may not be handled identically
For most projects this doesn't matter. For projects with complex decorator patterns or bleeding-edge TypeScript features, tsc is safer.
No isolated declarations requirement¶
With rules_ts, existing codebases work immediately — no code changes needed. With rules_typescript, you either:
- Add explicit return types to all exports (the intended path — enables the fast
.d.tsboundary) - Use
# gazelle:ts_isolated_declarations false(escape hatch — everything compiles but you lose the incremental boundary benefit)
Option 2 gets you running immediately, but without the speed advantage that justifies the ruleset. The migration from option 2 to option 1 is gradual (package by package, aided by the ESLint plugin) but non-trivial for large codebases.
Mature ecosystem¶
rules_ts is published on BCR, used in production by real companies, and battle-tested at scale. rules_typescript is v0.1.0. Expect rough edges.
npm handling¶
rules_js's pnpm virtual store with symlinks handles more edge cases than our lockfile parser:
- Nested node_modules patterns
- Complex peer dependency resolution
- Hoisting edge cases
Our parser handles the common cases (scoped packages, @types pairing, multiple versions, npm aliases, pnpm workspaces, dependency cycles) but exotic lockfile patterns may break.
Windows¶
rules_ts + rules_js work on Windows. Our node_modules build action is cross-platform (Node.js script), but test runners, dev server, and binary runners are still bash scripts.
Trade-offs: where rules_typescript is better¶
Compilation speed¶
Oxc (Rust) compiles TypeScript 10-100x faster per file than tsc. For a 500-file project, clean compilation is seconds, not minutes. Incremental rebuilds (touching one file) are sub-second.
Incremental boundary¶
The .d.ts isolated declarations boundary means changing a function body without changing its return type does NOT recompile any downstream package. This is architecturally impossible with tsc project references (which always re-check the dependency graph).
Vite-native¶
Bundling, dev server, HMR, React Fast Refresh, framework Vite plugins — all built-in. rules_ts has no bundler or dev server; you wire those yourself.
Simpler dependency chain¶
No rules_js. No complex virtual store. One pnpm-lock.yaml → one @npm repository.
Gazelle¶
Open-source BUILD file generation with 10 directives, framework auto-detection, codegen auto-detection, and automatic lint/dev-server/bundler target generation. rules_ts relies on the proprietary Aspect CLI.
Zero system prerequisites¶
Only Bazelisk needed. Node.js, pnpm, Go, Rust are all downloaded hermetically. rules_ts requires system Node.js and pnpm.
Migration steps¶
If you decide to migrate from rules_ts:
- Replace
ts_projecttargets withts_compile - Replace
js_library/npm_link_all_packageswithnpm_translate_lock - Remove
tsconfig.jsonfrom BUILD deps —ts_compilegenerates its tsconfig internally - Run
bazel run //:gazelleto regenerate BUILD files - If your codebase lacks explicit return types, add
# gazelle:ts_isolated_declarations falseto your root BUILD.bazel first (see Path B), then migrate package by package
Key conceptual differences¶
No tsconfig.json in BUILD files. ts_compile generates a tsconfig per target internally using rootDirs, moduleResolution: "Bundler", and paths entries from deps.
One @npm repository. rules_ts with rules_js creates per-package virtual stores. We create a single @npm repo. Labels: @npm//:react, @npm//:types_react.
Isolated declarations are opt-out. New projects start with isolated_declarations = True. Existing projects opt out with # gazelle:ts_isolated_declarations false, then gradually opt packages back in.
node_modules is automatic. ts_test builds its node_modules tree from deps automatically. No manual node_modules target needed (unless overriding for specific cases).