博客

代码转换入门:用 OXC Transformer 实现 TypeScript 编译

学习使用 OXC Transformer 将 TypeScript 编译成 JavaScript,支持 JSX 转换和语法降级,比 Babel 更快更简单。

LibDoc Team 2026年3月8日 OXC 专栏 57 分钟阅读
#oxc #transformer #typescript #babel #代码转换 #rust

代码转换入门:用 OXC Transformer 实现 TypeScript 编译

在前端开发中,我们经常需要”转换”代码:把 TypeScript 变成 JavaScript,把 ES6 语法变成 ES5,把 JSX 变成普通的 JavaScript 函数调用。

今天,我们来学习 OXC Transformer —— 一个比 Babel 更快的代码转换工具。

代码转换是什么?

为什么需要转换代码?

浏览器并不能直接运行所有我们写的代码:

源代码问题解决方案
TypeScript浏览器不认识类型语法剥离类型,转换为 JS
ES6+ 语法旧浏览器不支持降级到 ES5
JSX浏览器无法解析转换为 React.createElement

常见的转换场景

1. TypeScript → JavaScript

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

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

2. JSX → JavaScript

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

3. ES6+ → ES5

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

Transformer 能做什么?

OXC Transformer 提供以下能力:

  • 剥离 TypeScript 类型:将 TS 转换为纯 JS
  • 转换 JSX:将 JSX 语法转换为函数调用
  • 语法降级:将新语法转换为旧语法(部分支持)
  • 保留源码映射:生成 Source Map 便于调试

快速开始

安装

npm install @oxc-transform

基本使用

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);

输出:

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

异步转换

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

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

console.log(result.code);

实战:将 TypeScript 编译成 JavaScript

让我们完整演练一遍 TypeScript 编译流程。

步骤一:创建 TypeScript 项目

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

创建 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();
  }
}

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

export { ApiClient, Config, HttpMethod };

步骤二:创建编译脚本

创建 build.js

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

function compileTypeScript(inputPath, outputPath) {
  // 读取源文件
  const code = fs.readFileSync(inputPath, "utf-8");

  // 转换
  const result = transformSync(code, {
    lang: "ts",
    // 保留 JSX(如果有的话)
    jsx: "react",
    // 目标 ES 版本
    target: "es2020",
    // 生成 Source Map
    sourcemap: true,
  });

  // 确保输出目录存在
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // 写入输出文件
  fs.writeFileSync(outputPath, result.code);

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

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

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

步骤三:运行编译

node build.js

输出:

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

查看生成的 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 };

可以看到:

  • TypeScript 类型定义被移除了
  • private 字段变成了 # 私有字段语法
  • 代码结构保持不变

配置选项详解

lang - 语言类型

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

target - 目标 ES 版本

transformSync(code, {
  // 支持的目标版本
  target: "es5",
  target: "es2015",
  target: "es2016",
  target: "es2017",
  target: "es2018",
  target: "es2019",
  target: "es2020",
  target: "es2021",
  target: "es2022",
  target: "esnext",
});

jsx - JSX 转换模式

// React 经典模式
transformSync(code, {
  jsx: "react",  // React.createElement(...)
});

// React 自动运行时
transformSync(code, {
  jsx: "automatic",
  jsxImportSource: "react",  // 自动导入
});

// 自定义 JSX 工厂
transformSync(code, {
  jsx: "react",
  jsxFactory: "h",           // h(...) 而不是 React.createElement
  jsxFragment: "Fragment",
});

sourcemap - 源码映射

transformSync(code, {
  sourcemap: true,    // 生成 Source Map
  sourcemap: "inline", // 内联 Source Map
});

JSX 转换详解

OXC Transformer 完整支持 JSX 转换。

React 经典模式

// 输入
function App() {
  return (
    <div className="container">
      <h1>Hello, World!</h1>
      <button onClick={() => alert("clicked")}>Click me</button>
    </div>
  );
}
// 输出(经典模式)
function App() {
  return React.createElement(
    "div",
    { className: "container" },
    React.createElement("h1", null, "Hello, World!"),
    React.createElement("button", { onClick: () => alert("clicked") }, "Click me")
  );
}

React 自动运行时

// 配置
transformSync(code, {
  lang: "jsx",
  jsx: "automatic",
  jsxImportSource: "react",
});
// 输出(自动运行时)
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",
});

实战小项目:批量编译

让我们创建一个更实用的批量编译工具。

创建批量编译脚本

// 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,
  });

  // 确保输出目录存在
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // 改变文件扩展名 .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");

  // 清理输出目录
  if (fs.existsSync(config.outputDir)) {
    fs.rmSync(config.outputDir, { recursive: true });
  }
  fs.mkdirSync(config.outputDir, { recursive: true });

  // 编译所有文件
  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();

添加到 package.json

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

与 Babel 的对比

功能对比

功能BabelOXC Transformer
TypeScript 编译
JSX 转换
语法降级✅ 完整🚧 部分支持
插件系统✅ 丰富❌ 暂无
Polyfill 注入
自定义转换

性能对比

// 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");

结果:

OXC Transformer: 150ms
Babel: 2800ms

OXC Transformer 比 Babel 快约 18 倍!

迁移建议

场景建议
纯 TypeScript 编译OXC 完全够用
简单 JSX 项目OXC 可以满足
需要语法降级到 ES5暂时使用 Babel
需要自定义插件继续使用 Babel
大型项目渐进迁移可混合使用

混合使用方案

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

常见问题

如何处理装饰器?

OXC 目前对装饰器的支持有限。如果项目使用装饰器,建议暂时使用 Babel 或 TypeScript 编译器。

生成的代码格式不好看?

OXC Transformer 专注于转换,不负责格式化。可以配合 Oxfmt 使用:

# 先转换,再格式化
oxc-transform ./src --out-dir ./dist
oxfmt "./dist/**/*.js" --write

Source Map 不工作?

确保在转换时启用了 Source Map,并且路径正确:

transformSync(code, {
  sourcemap: true,
  filename: "source.ts",  // 指定源文件名
});

与打包工具集成

Vite 集成

OXC 提供了官方的 Vite 插件:

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

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

Rollup 集成

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

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

总结

本文介绍了 OXC Transformer 的核心用法:

内容说明
TypeScript 编译剥离类型,生成 JS
JSX 转换转换为 React.createElement 或自动运行时
配置选项lang, target, jsx, sourcemap
性能优势比 Babel 快 18 倍以上

Transformer 的核心价值:用 Rust 的速度完成代码转换。

下一步

想了解更多 Transformer 的配置选项?访问 OXC 中文文档 - Transformer 章节

下一篇文章,我们将学习 OXC Minifier —— 让你的生产环境代码更小更快!


💡 相关阅读