ts-blank-space vs Node.js --strip-types vs swc: TypeScript Type Stripping (2026)
TL;DR
ts-blank-space (by Bloomberg) replaces TypeScript type annotations with whitespace — preserving exact source positions for perfect source maps, zero AST transformation. Node.js --experimental-strip-types (Node 22.6+) strips types natively at runtime — no build step, no dependencies, just node file.ts. @swc/core is the Rust-based TypeScript transpiler — strips types AND downlevels syntax, 20x faster than tsc. In 2026: Node.js --strip-types for scripts and development, swc for production builds, ts-blank-space for source-map-perfect debugging.
Key Takeaways
- ts-blank-space: ~50K weekly downloads — replaces types with spaces (identical byte positions), Bloomberg project
- Node.js --strip-types: built-in (Node 22.6+) — zero config, just run
node --strip-types file.ts - @swc/core: ~30M weekly downloads — full transpiler, type stripping + syntax downlevel + minification
- Type stripping = remove types, keep everything else (no enum transform, no decorator emit)
- ts-blank-space preserves exact byte offsets — debugger breakpoints match source exactly
- Node.js strip-types uses swc under the hood but only strips types (no transforms)
What Is Type Stripping?
// Source TypeScript:
function getScore(name: string, threshold: number = 80): boolean {
const score: number = calculateHealthScore(name)
return score >= threshold
}
// After type stripping (replace types with whitespace):
function getScore(name , threshold = 80) {
const score = calculateHealthScore(name)
return score >= threshold
}
// After traditional transpilation (rewrite AST):
function getScore(name, threshold = 80) {
const score = calculateHealthScore(name);
return score >= threshold;
}
// Type stripping is SIMPLER:
// - No AST transformation
// - Types become whitespace (same byte positions)
// - Source maps are trivial (or unnecessary)
// - Much faster than full transpilation
What Type Stripping CANNOT Handle
// These TypeScript features require CODE TRANSFORMATION, not just erasure:
// ❌ Enums (generate runtime code):
enum Status { Active, Inactive }
// Must become: var Status; (function(Status) { ... })(Status)
// ❌ Namespaces with values:
namespace Utils { export function parse() {} }
// Must become: var Utils; (function(Utils) { ... })(Utils)
// ❌ Parameter properties:
class Foo { constructor(public name: string) {} }
// Must become: class Foo { constructor(name) { this.name = name; } }
// ❌ Legacy decorators (experimentalDecorators):
@Injectable()
class Service {}
// ✅ These are fine (just erase):
// Type annotations, interfaces, type aliases, generics, as casts,
// satisfies, declare, abstract, readonly, override
ts-blank-space
ts-blank-space — Bloomberg's type eraser:
How it works
// Input (byte positions matter):
const x: number = 42
// ^^^^^^^^^ these bytes become spaces
// Output:
const x = 42
// ^^^^^^^^^ exact same positions — debugger breakpoints match!
// Compare to swc/esbuild:
const x = 42
// ← positions shifted — source map needed for debugging
Usage as a library
import tsBlankSpace from "ts-blank-space"
const tsCode = `
function greet(name: string): void {
console.log(\`Hello, \${name}!\`)
}
`
const jsCode = tsBlankSpace(tsCode)
// Output:
// function greet(name ) {
// console.log(`Hello, ${name}!`)
// }
// Source positions are IDENTICAL — no source map needed
Node.js loader
# Register as Node.js loader:
node --import ts-blank-space/register ./src/index.ts
# Or in package.json:
{
"scripts": {
"dev": "node --import ts-blank-space/register src/index.ts"
}
}
With Vitest
// vitest.config.ts
import { defineConfig } from "vitest/config"
export default defineConfig({
esbuild: false,
plugins: [
{
name: "ts-blank-space",
transform(code, id) {
if (!id.endsWith(".ts") && !id.endsWith(".tsx")) return
const { default: transform } = await import("ts-blank-space")
return { code: transform(code), map: null } // No map needed!
},
},
],
})
When it shines: debugging
Traditional transpiler (swc, esbuild):
Source: line 42, column 15
Output: line 38, column 10 ← shifted
Need: source map to map back
ts-blank-space:
Source: line 42, column 15
Output: line 42, column 15 ← exact same!
Need: nothing — breakpoints work perfectly
Node.js --experimental-strip-types
Node.js type stripping — built-in since Node 22.6:
Usage
# Node.js 22.6+ (experimental):
node --experimental-strip-types src/index.ts
# Node.js 23.6+ (stable, no flag needed):
node src/index.ts
# With tsconfig paths (Node 23+):
node --experimental-transform-types src/index.ts
What it does
node src/index.ts:
1. Detects .ts extension
2. Runs type stripping (via amaro/swc internally)
3. Executes the resulting JavaScript
4. NO build step, NO node_modules transpiler, NO config
Limitations:
- Only strips types (no enum, namespace, or decorator transform)
- No JSX support (.tsx)
- No tsconfig paths resolution (use --experimental-transform-types)
- Cannot import .ts from .js files without flag
package.json setup
{
"type": "module",
"scripts": {
"start": "node src/index.ts",
"dev": "node --watch src/index.ts"
},
"engines": {
"node": ">=23.6.0"
}
}
With --experimental-transform-types
# Enables enum and namespace transformation (not just stripping):
node --experimental-transform-types src/index.ts
# Now enums work:
enum Status { Active = "active", Inactive = "inactive" }
// Transformed to runtime code automatically
Comparison with tsx
# tsx (via esbuild — full transpilation):
npx tsx src/index.ts
# ✅ Handles: enums, decorators, JSX, tsconfig paths
# ✅ Works on all Node.js versions
# ❌ External dependency
# node --strip-types (built-in):
node src/index.ts
# ✅ No dependencies
# ✅ Perfect for simple TypeScript
# ❌ No enums (without --transform-types)
# ❌ No JSX
# ❌ Requires Node 23.6+
@swc/core
@swc/core — Rust-based TypeScript compiler:
Type stripping mode
import { transform } from "@swc/core"
const result = await transform(tsCode, {
jsc: {
parser: { syntax: "typescript", tsx: false },
target: "es2022",
transform: null, // No transformations — just strip types
},
sourceMaps: true,
})
console.log(result.code)
console.log(result.map)
Full transpilation (production builds)
import { transform } from "@swc/core"
const result = await transform(tsCode, {
jsc: {
parser: {
syntax: "typescript",
tsx: true,
decorators: true,
},
target: "es2022",
transform: {
decoratorVersion: "2022-03",
react: { runtime: "automatic" },
},
},
module: { type: "es6" },
sourceMaps: true,
minify: true,
})
Performance comparison
Transpiling 1000 TypeScript files:
tsc (TypeScript compiler): ~12,000 ms
esbuild (Go): ~200 ms
@swc/core (Rust): ~180 ms
ts-blank-space (JS): ~50 ms (type strip only)
Node.js --strip-types: ~0 ms (on-demand per file)
For type stripping only:
ts-blank-space is fastest (no AST parse for simple cases)
Node.js built-in is zero-overhead (amortized per file)
For full transpilation:
swc and esbuild are comparable (~20x faster than tsc)
Used by bundlers
swc powers:
- Next.js (default compiler since v12)
- Vite (via @vitejs/plugin-react-swc)
- Parcel 2
- rspack / Rsbuild
You might already be using swc without knowing it
Feature Comparison
| Feature | ts-blank-space | Node.js --strip-types | @swc/core |
|---|---|---|---|
| Type stripping | ✅ | ✅ | ✅ |
| Enum transform | ❌ | ⚠️ (--transform-types) | ✅ |
| Decorator transform | ❌ | ❌ | ✅ |
| JSX / TSX | ❌ | ❌ | ✅ |
| Source maps needed | ❌ (same positions) | ❌ | ✅ |
| Minification | ❌ | ❌ | ✅ |
| Dependencies | 0 | 0 (built-in) | Native binary |
| Node.js version | Any | 23.6+ | Any |
| Speed (type strip) | ~50ms/1000 files | On-demand | ~180ms/1000 files |
| Weekly downloads | ~50K | built-in | ~30M |
When to Use Each
Use ts-blank-space if:
- Need perfect source-map-free debugging (breakpoints match exactly)
- Building tools that need position-preserving transforms
- Want zero source map overhead in development
Use Node.js --strip-types if:
- Running scripts, CLI tools, or small servers — zero config, zero deps
- Node.js 23.6+ is available
- Don't use enums, decorators, or JSX
- Simplest possible TypeScript execution
Use @swc/core if:
- Production builds that need minification and bundling
- Using enums, decorators, JSX, or legacy syntax
- Need source maps for production error tracking
- Already using Next.js, Vite with SWC, or rspack (you're already using it)
Also consider:
- tsx — wraps esbuild for running TypeScript, handles everything including paths
- esbuild — similar to swc, excellent for bundling
Methodology
Download data from npm registry (weekly average, February 2026). Performance benchmarks on Node.js 22 with 1000 TypeScript files. Feature comparison based on ts-blank-space v0.x, Node.js 23.6, and @swc/core v1.x.