Skip to main content

ts-blank-space vs Node.js --strip-types vs swc: TypeScript Type Stripping (2026)

·PkgPulse Team

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

Featurets-blank-spaceNode.js --strip-types@swc/core
Type stripping
Enum transform⚠️ (--transform-types)
Decorator transform
JSX / TSX
Source maps needed❌ (same positions)
Minification
Dependencies00 (built-in)Native binary
Node.js versionAny23.6+Any
Speed (type strip)~50ms/1000 filesOn-demand~180ms/1000 files
Weekly downloads~50Kbuilt-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.

Compare TypeScript tooling and build packages on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.