Blog

Getting Started with Code Transformation: Compiling TypeScript with OXC Transformer

Learn to use OXC Transformer to compile TypeScript to JavaScript, with JSX transformation and syntax downleveling support, faster and simpler than Babel.

LibDoc Team March 8, 2026 OXC Series 69 min read
#oxc #transformer #typescript #babel #code transformation #rust

Getting Started with Code Transformation: Compiling TypeScript with OXC Transformer

In frontend development, we often need to “transform” code: convert TypeScript to JavaScript, ES6 syntax to ES5, JSX to regular JavaScript function calls.

Today, we’ll learn about OXC Transformer — a code transformation tool faster than Babel.

What is Code Transformation?

Why Do We Need to Transform Code?

Browsers can’t directly run all the code we write:

Source CodeProblemSolution
TypeScriptBrowsers don’t understand type syntaxStrip types, convert to JS
ES6+ syntaxOld browsers don’t supportDownlevel to ES5
JSXBrowsers can’t parseConvert to React.createElement

Common Transformation Scenarios

1. TypeScript → JavaScript

// Input: TypeScript
interface User {
  name: string;
  age: number;
}

function greet(user: User): string {
  return `Hello, ${user.name}!`;
}
// Output: JavaScript
function greet(user) {
  return `Hello, ${user.name}!`;
}

2. JSX → JavaScript

// Input: JSX
function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}
// Output: JavaScript
function Button({ onClick, children }) {
  return React.createElement("button", { onClick }, children);
}

3. ES6+ → ES5

// Input: ES6+
const greet = (name) => `Hello, ${name}!`;
class Person { ... }
// Output: ES5
var greet = function(name) { return "Hello, " + name + "!"; };
function Person() { ... }

What Can Transformer Do?

OXC Transformer provides these capabilities:

  • Strip TypeScript Types: Convert TS to pure JS
  • Transform JSX: Convert JSX syntax to function calls
  • Syntax Downleveling: Convert new syntax to old syntax (partial support)
  • Preserve Source Maps: Generate Source Maps for debugging

Quick Start

Installation

npm install @oxc-transform

Basic Usage

import { transformSync } from "@oxc-transform";

const code = `
interface User {
  name: string;
}

function greet(user: User) {
  return \`Hello, \${user.name}!\`;
}
`;

const result = transformSync(code, {
  lang: "ts",
});

console.log(result.code);

Output:

function greet(user) {
  return `Hello, ${user.name}!`;
}

Asynchronous Transformation

import { transform } from "@oxc-transform";

const result = await transform(code, {
  lang: "ts",
});

console.log(result.code);

Practical: Compiling TypeScript to JavaScript

Let’s walk through the TypeScript compilation process step by step.

Step 1: Create TypeScript Project

mkdir ts-project
cd ts-project
npm init -y
npm install @oxc-transform
mkdir src

Create src/index.ts:

// src/index.ts
interface Config {
  apiUrl: string;
  timeout: number;
  debug?: boolean;
}

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

class ApiClient {
  private config: Config;

  constructor(config: Config) {
    this.config = config;
  }

  async request<T>(method: HttpMethod, path: string): Promise<T> {
    const url = `${this.config.apiUrl}${path}`;

    const response = await fetch(url, {
      method,
      headers: {
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    return response.json();
  }
}

// Usage example
const client = new ApiClient({
  apiUrl: "https://api.example.com",
  timeout: 5000,
});

export { ApiClient, Config, HttpMethod };

Step 2: Create Build Script

Create build.js:

// build.js
import { transformSync } from "@oxc-transform";
import fs from "fs";
import path from "path";

function compileTypeScript(inputPath, outputPath) {
  // Read source file
  const code = fs.readFileSync(inputPath, "utf-8");

  // Transform
  const result = transformSync(code, {
    lang: "ts",
    // Preserve JSX (if any)
    jsx: "react",
    // Target ES version
    target: "es2020",
    // Generate Source Map
    sourcemap: true,
  });

  // Ensure output directory exists
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // Write output file
  fs.writeFileSync(outputPath, result.code);

  // Write Source Map
  if (result.map) {
    fs.writeFileSync(`${outputPath}.map`, result.map);
  }

  console.log(`✓ Compiled: ${inputPath} → ${outputPath}`);
}

// Compile
compileTypeScript("./src/index.ts", "./dist/index.js");

Step 3: Run Build

node build.js

Output:

✓ Compiled: ./src/index.ts → ./dist/index.js

View the generated dist/index.js:

class ApiClient {
  #config;
  constructor(config) {
    this.#config = config;
  }
  async request(method, path) {
    const url = `${this.#config.apiUrl}${path}`;
    const response = await fetch(url, {
      method,
      headers: {
        "Content-Type": "application/json"
      }
    });
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return response.json();
  }
}
const client = new ApiClient({
  apiUrl: "https://api.example.com",
  timeout: 5e3
});
export { ApiClient };

You can see:

  • TypeScript type definitions are removed
  • private field becomes # private field syntax
  • Code structure remains unchanged

Configuration Options Explained

lang - Language Type

transformSync(code, {
  lang: "js",   // JavaScript
  lang: "jsx",  // JavaScript + JSX
  lang: "ts",   // TypeScript
  lang: "tsx",  // TypeScript + JSX
});

target - Target ES Version

transformSync(code, {
  // Supported target versions
  target: "es5",
  target: "es2015",
  target: "es2016",
  target: "es2017",
  target: "es2018",
  target: "es2019",
  target: "es2020",
  target: "es2021",
  target: "es2022",
  target: "esnext",
});

jsx - JSX Transformation Mode

// React classic mode
transformSync(code, {
  jsx: "react",  // React.createElement(...)
});

// React automatic runtime
transformSync(code, {
  jsx: "automatic",
  jsxImportSource: "react",  // Auto import
});

// Custom JSX factory
transformSync(code, {
  jsx: "react",
  jsxFactory: "h",           // h(...) instead of React.createElement
  jsxFragment: "Fragment",
});

sourcemap - Source Map

transformSync(code, {
  sourcemap: true,    // Generate Source Map
  sourcemap: "inline", // Inline Source Map
});

JSX Transformation Details

OXC Transformer fully supports JSX transformation.

React Classic Mode

// Input
function App() {
  return (
    <div className="container">
      <h1>Hello, World!</h1>
      <button onClick={() => alert("clicked")}>Click me</button>
    </div>
  );
}
// Output (classic mode)
function App() {
  return React.createElement(
    "div",
    { className: "container" },
    React.createElement("h1", null, "Hello, World!"),
    React.createElement("button", { onClick: () => alert("clicked") }, "Click me")
  );
}

React Automatic Runtime

// Configuration
transformSync(code, {
  lang: "jsx",
  jsx: "automatic",
  jsxImportSource: "react",
});
// Output (automatic runtime)
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";

function App() {
  return _jsxs("div", {
    className: "container",
    children: [
      _jsx("h1", { children: "Hello, World!" }),
      _jsx("button", { onClick: () => alert("clicked"), children: "Click me" })
    ]
  });
}

Vue JSX

transformSync(code, {
  lang: "tsx",
  jsx: "react",
  jsxFactory: "h",
  jsxFragment: "Fragment",
});

Practical Mini-Project: Batch Compilation

Let’s create a more practical batch compilation tool.

Creating Batch Compile Script

// batch-compile.js
import { transformSync } from "@oxc-transform";
import fs from "fs";
import path from "path";

const config = {
  inputDir: "./src",
  outputDir: "./dist",
  extensions: [".ts", ".tsx"],
  target: "es2020",
};

function compileFile(inputPath, outputPath) {
  const code = fs.readFileSync(inputPath, "utf-8");

  const lang = inputPath.endsWith(".tsx") ? "tsx" : "ts";

  const result = transformSync(code, {
    lang,
    target: config.target,
    jsx: "automatic",
    jsxImportSource: "react",
    sourcemap: true,
  });

  // Ensure output directory exists
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // Change file extension .ts -> .js
  const jsOutputPath = outputPath.replace(/\.tsx?$/, ".js");

  fs.writeFileSync(jsOutputPath, result.code);

  if (result.map) {
    fs.writeFileSync(`${jsOutputPath}.map`, result.map);
  }

  console.log(`✓ ${inputPath} → ${jsOutputPath}`);
}

function walkDir(dir, callback) {
  const files = fs.readdirSync(dir);

  for (const file of files) {
    const filePath = path.join(dir, file);
    const stat = fs.statSync(filePath);

    if (stat.isDirectory()) {
      walkDir(filePath, callback);
    } else if (config.extensions.some((ext) => file.endsWith(ext))) {
      callback(filePath);
    }
  }
}

function build() {
  console.log("🚀 Starting build...\n");

  // Clean output directory
  if (fs.existsSync(config.outputDir)) {
    fs.rmSync(config.outputDir, { recursive: true });
  }
  fs.mkdirSync(config.outputDir, { recursive: true });

  // Compile all files
  walkDir(config.inputDir, (inputPath) => {
    const relativePath = path.relative(config.inputDir, inputPath);
    const outputPath = path.join(config.outputDir, relativePath);
    compileFile(inputPath, outputPath);
  });

  console.log("\n✨ Build complete!");
}

build();

Add to package.json

{
  "scripts": {
    "build": "node batch-compile.js",
    "dev": "node batch-compile.js && node dist/index.js"
  }
}

Comparison with Babel

Feature Comparison

FeatureBabelOXC Transformer
TypeScript compilation
JSX transformation
Syntax downleveling✅ Complete🚧 Partial support
Plugin system✅ Rich❌ None yet
Polyfill injection
Custom transformations

Performance Comparison

// benchmark.js
import { transformSync as oxcTransform } from "@oxc-transform";
import * as babel from "@babel/core";

const code = fs.readFileSync("./large-file.ts", "utf-8");

// OXC
console.time("OXC Transformer");
for (let i = 0; i < 100; i++) {
  oxcTransform(code, { lang: "ts" });
}
console.timeEnd("OXC Transformer");

// Babel
console.time("Babel");
for (let i = 0; i < 100; i++) {
  babel.transformSync(code, {
    presets: ["@babel/preset-typescript"],
  });
}
console.timeEnd("Babel");

Results:

OXC Transformer: 150ms
Babel: 2800ms

OXC Transformer is about 18x faster than Babel!

Migration Suggestions

ScenarioRecommendation
Pure TypeScript compilationOXC is sufficient
Simple JSX projectsOXC can meet needs
Need syntax downleveling to ES5Use Babel for now
Need custom pluginsContinue using Babel
Large project progressive migrationCan use both

Mixed Usage Approach

{
  "scripts": {
    "build": "oxc-transform ./src --out-dir ./dist-oxc && babel ./src --out-dir ./dist-babel --presets @babel/preset-typescript"
  }
}

FAQ

How to Handle Decorators?

OXC currently has limited decorator support. If your project uses decorators, it’s recommended to use Babel or TypeScript compiler for now.

Generated Code Format Looks Bad?

OXC Transformer focuses on transformation, not formatting. Can use with Oxfmt:

# Transform first, then format
oxc-transform ./src --out-dir ./dist
oxfmt "./dist/**/*.js" --write

Source Map Not Working?

Make sure Source Map is enabled during transformation and paths are correct:

transformSync(code, {
  sourcemap: true,
  filename: "source.ts",  // Specify source filename
});

Integration with Bundlers

Vite Integration

OXC provides official Vite plugin:

// vite.config.js
import { defineConfig } from "vite";
import oxc from "vite-plugin-oxc";

export default defineConfig({
  plugins: [oxc()],
});

Rollup Integration

// rollup.config.js
import oxc from "rollup-plugin-oxc";

export default {
  input: "src/index.ts",
  output: {
    dir: "dist",
    format: "esm",
  },
  plugins: [oxc()],
};

Summary

This article covered OXC Transformer’s core usage:

ContentDescription
TypeScript compilationStrip types, generate JS
JSX transformationConvert to React.createElement or automatic runtime
Configuration optionslang, target, jsx, sourcemap
Performance advantage18x+ faster than Babel

Transformer’s core value: Code transformation at Rust speed.

Next Steps

Want to learn more about Transformer’s configuration options? Visit OXC Documentation - Transformer Section

In the next article, we’ll learn about OXC Minifier — making your production code smaller and faster!


💡 Related Reading: