ブログ

コード変換入門:OXC TransformerでTypeScriptをコンパイル

OXC Transformerを使ってTypeScriptをJavaScriptにコンパイルする方法を学びます。JSX変換と構文ダウングレードをサポートし、Babelより高速でシンプル。

LibDoc Team 2026年3月8日 OXC 連載 59 分で読める
#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コンパイルの流れを一通り演练しましょう。

ステップ1: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 };

ステップ2:コンパイルスクリプトの作成

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",
    // ソースマップを生成
    sourcemap: true,
  });

  // 出力ディレクトリを確保
  const outputDir = path.dirname(outputPath);
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  // 出力ファイルを書き込む
  fs.writeFileSync(outputPath, result.code);

  // ソースマップを書き込む
  if (result.map) {
    fs.writeFileSync(`${outputPath}.map`, result.map);
  }

  console.log(`✓ コンパイル完了: ${inputPath} → ${outputPath}`);
}

// コンパイル実行
compileTypeScript("./src/index.ts", "./dist/index.js");

ステップ3:コンパイルの実行

node build.js

出力:

✓ コンパイル完了: ./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",           // React.createElementではなくh(...)
  jsxFragment: "Fragment",
});

sourcemap - ソースマップ

transformSync(code, {
  sourcemap: true,    // ソースマップを生成
  sourcemap: "inline", // インラインソースマップ
});

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("🚀 ビルド開始...\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();

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

ソースマップが動作しない?

変換時にソースマップを有効にし、パスが正しいことを確認:

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 を学びます — 本番環境のコードをより小さくより速く!


💡 関連記事