9.2 Fast Transpilers — esbuild, swc, tsx
The Limits of tsc and the Rise of Transpilers
tsc (TypeScript Compiler) performs both type checking and code transformation. In large projects, this can become slow.
Fast transpilers skip type checking and only handle TypeScript → JavaScript conversion, dramatically increasing speed.
| Tool | Language | Speed | Type Checking |
|---|---|---|---|
tsc | TypeScript | Normal | ✅ Included |
esbuild | Go | Very fast (100x) | ❌ None |
swc | Rust | Very fast (70x) | ❌ None |
tsx | (esbuild-based) | Very fast | ❌ None |
Core principle: Use transpilers for development speed, use separate tsc type checking for type safety.
esbuild
An ultra-fast bundler and transpiler written in Go.
Installation
npm install --save-dev esbuild
Basic Usage
# TypeScript → JavaScript transpilation
npx esbuild src/index.ts --outfile=dist/index.js
# Bundling
npx esbuild src/index.ts --bundle --outfile=dist/bundle.js
# Minification
npx esbuild src/index.ts --bundle --minify --outfile=dist/bundle.min.js
# Watch mode
npx esbuild src/index.ts --bundle --watch --outfile=dist/bundle.js
Node.js Server Build
// build.ts
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'node',
target: 'node20',
format: 'cjs', // or 'esm'
outfile: 'dist/index.js',
external: [ // Packages to exclude from bundle
'express',
'pg',
],
sourcemap: true,
});
console.log('Build complete!');
Externalizing All node_modules (Useful for Node.js Servers)
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'node',
packages: 'external', // Externalize all node_modules
outfile: 'dist/index.js',
});
Using Plugins
import * as esbuild from 'esbuild';
import { TsconfigPathsPlugin } from '@esbuild-plugins/tsconfig-paths';
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
plugins: [
TsconfigPathsPlugin({ tsconfig: './tsconfig.json' }),
],
outfile: 'dist/index.js',
});
package.json Scripts
{
"scripts": {
"build": "node build.js",
"build:types": "tsc --emitDeclarationOnly",
"dev": "node --watch dist/index.js",
"typecheck": "tsc --noEmit"
}
}
swc (Speedy Web Compiler)
A transpiler written in Rust, serving as an alternative to Babel. Used internally by Next.js.
Installation
npm install --save-dev @swc/core @swc/cli
.swcrc Configuration
// .swcrc
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true
},
"target": "es2022",
"transform": {
"react": {
"runtime": "automatic" // React 17+ JSX transform
}
}
},
"module": {
"type": "commonjs" // or "es6"
},
"sourceMaps": true
}
Basic Usage
# Transform single file
npx swc src/index.ts -o dist/index.js
# Transform entire directory
npx swc src -d dist
# Watch mode
npx swc src -d dist --watch
Using with Jest (jest-transform)
npm install --save-dev @swc/jest
// jest.config.js
module.exports = {
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
};
Test execution speed is 3–5x faster than Babel + ts-jest.
Using swc with NestJS
# NestJS CLI with swc build
nest build --webpack # or
npx @nestjs/cli build --builder swc
// nest-cli.json
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"builder": {
"type": "swc",
"options": {
"swcrcPath": ".swcrc"
}
}
}
}
tsx
A Node.js TypeScript runner based on esbuild. Much faster than ts-node.
Installation
npm install --save-dev tsx
Basic Usage
# Run TypeScript files directly
npx tsx src/index.ts
# Watch mode (auto-restart on file changes)
npx tsx watch src/index.ts
# REPL
npx tsx
package.json Scripts
{
"scripts": {
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
"build": "tsc",
"typecheck": "tsc --noEmit"
}
}
Comparison with ts-node
# ts-node: slow (same approach as tsc)
npx ts-node src/index.ts
# tsx: fast (uses esbuild, no type checking)
npx tsx src/index.ts
Using tsx in ESM Mode
// package.json
{
"type": "module"
}
# Run ESM TypeScript files
npx tsx src/index.ts # Automatically handles ESM
Script Execution Pattern
// scripts/seed-db.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.user.createMany({
data: [
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
],
});
console.log('Seeded!');
}
main().finally(() => prisma.$disconnect());
{
"scripts": {
"seed": "tsx scripts/seed-db.ts"
}
}
Trade-off Comparison
When to Use Each Tool
tsc (type checking only):
- Type validation in CI/CD pipelines
- Generating .d.ts for library publishing
- "typecheck" script
esbuild (bundle builds):
- Fast production builds
- Serverless function bundling
- When bundle size matters
swc (babel replacement):
- Improving Jest transformation speed
- NestJS development server
- Migrating from Babel plugins
tsx (development execution):
- Fast Node.js script execution
- Development server hot reload
- Running migration scripts
Strategy: Type Check + Fast Build Combination
// package.json — Recommended script setup
{
"scripts": {
"dev": "tsx watch src/index.ts", // Dev: fast execution
"build": "tsc -p tsconfig.build.json && tsc-alias", // Build: with type check
"build:fast": "esbuild src/index.ts --bundle --outfile=dist/index.js", // CI fast build
"typecheck": "tsc --noEmit", // Type check only (separate CI step)
"test": "vitest" // Tests: Vite-based (esbuild built-in)
}
}
Real-World Development Environment Setup
Optimal Node.js API Server Configuration
// package.json
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "esbuild src/index.ts --bundle --platform=node --packages=external --outfile=dist/index.js --sourcemap",
"start": "node dist/index.js",
"typecheck": "tsc --noEmit"
}
}
Development flow:
npm run dev— Start instantly with tsx (no type check, fast)npm run typecheck— Check for type errors separatelynpm run build— Fast production build with esbuild
CI/CD Pipeline
# .github/workflows/ci.yml
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- run: npm run typecheck # Type checking
build:
runs-on: ubuntu-latest
steps:
- run: npm run build # Build
- run: npm test # Tests (swc-based jest)
Pro Tips
1. Vite internally uses esbuild
Using Vite gives you an ultra-fast development server without separate esbuild configuration.
2. Separate type checking and emit in tsconfig
// tsconfig.json (for type checking)
{ "noEmit": true }
// tsconfig.build.json (for builds)
{ "extends": "./tsconfig.json", "noEmit": false }
3. Measuring speed
# Measure tsc build time
time tsc --noEmit
# Measure esbuild build time
time npx esbuild src/index.ts --bundle --outfile=/dev/null
Generally, esbuild/swc is 50–100x faster than tsc.