跳到主要內容
← 回到學習

學習 · 程式語言

tsx 轉 jsx

目錄 · 10 個段落
  1. 01何時需要轉
  2. 02安裝 TypeScript
  3. 03一條指令轉換
  4. 04--jsx 三種模式
  5. 05想保留更多 ES 特性
  6. 06完整 tsconfig 寫法(若要常用)
  7. 07Type 註解會怎樣?
  8. 08替代:用 --declaration 同時生 .d.ts
  9. 09實際使用注意
  10. 10反方向:JSX 轉 TSX

TSX 轉 JSX

TL;DR
用 npx tsc --jsx preserve 把 TS React 檔降級成 JS,用於需要 交付給非 TypeScript 環境 的場景。2026 年比較少需要,因為大部分 toolchain(Vite / Next.js / Rspack)都直接支援 TS。
主要參考
Stack Overflow — How do I convert TSX to JSX

何時需要轉

通常不需要 (現代環境都支援 TS)。但有時候會需要:

  • 交付給 只接受 JS 的舊系統
  • 把 TypeScript 寫的 範例 code 給只懂 JS 的學員 / 文件
  • 整合到 遺留 build pipeline 不能升級
  • 提交給 只接受 plain JS 的代碼審查 系統
大部分情況不需要
2026 年的現實:Webpack / Vite / Next.js / Bun / Deno 都直接吃 .tsx。除非真的有舊系統限制,否則別轉,維護 TS 比 JS 好太多。

安裝 TypeScript

npm install -g typescript
 
# 或本地用
npm install --save-dev typescript
bash

一條指令轉換

npx tsc --jsx preserve -t es2020 --outDir js --noEmit false
bash
Flag作用
--jsx preserve保留 <div> 語法,輸出 .jsx(不是 React.createElement)
-t es2020編譯目標 ES 版本(保留 import / async / await)
--outDir js輸出資料夾
--noEmit false強制輸出檔案(Expo 預設 noEmit:true,要 override)

執行後會在 js/ 資料夾看到:

js/
├── App.jsx
├── components/
│   ├── Header.jsx
│   └── Footer.jsx
└── utils.js                    // 純 .ts 檔變 .js

--jsx 三種模式

模式輸出用途
preserve.jsx(語法保留)交給其他 transpiler 處理
reactReact.createElement(...)舊 React
react-jsx_jsx(...)新 React 17+ runtime
react-jsxdev_jsxDEV(...)dev 模式(含 source 資訊)

想交付 JSX 給 React 環境用,選 preserve。讓接收端的 toolchain 處理 React runtime。


想保留更多 ES 特性

# 保留 ES2022 特性(top-level await / private fields)
npx tsc --jsx preserve -t es2022 --outDir js --noEmit false
 
# 保留 ESNext(最新)
npx tsc --jsx preserve -t esnext --outDir js --noEmit false
 
# 同時把 module 系統留 ESM
npx tsc --jsx preserve -t es2020 --module esnext --outDir js --noEmit false
bash

-t 越新 → 保留越多現代語法。但接收端要支援該版本。


完整 tsconfig 寫法(若要常用)

// tsconfig.export-jsx.json
{
  "compilerOptions": {
    "jsx": "preserve",
    "target": "es2020",
    "module": "esnext",
    "outDir": "./js",
    "rootDir": "./src",
    "noEmit": false,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}
json
npx tsc -p tsconfig.export-jsx.json
bash

適合需要重複使用的轉換 pipeline。


Type 註解會怎樣?

--jsx preserve 還是會 strip 掉 type annotation:

// 原始 TSX
interface Props {
  name: string;
  age: number;
}
 
function Greet({ name, age }: Props) {
  return <h1>{name} is {age}</h1>;
}
typescript
// 輸出 JSX
function Greet({ name, age }) {
    return <h1>{name} is {age}</h1>;
}
// interface Props 直接消失
jsx

tsc 永遠 strip types,沒辦法把 type 留在 JSX 裡。


替代:用 --declaration 同時生 .d.ts

如果你還想保留 type 給未來用:

npx tsc --jsx preserve --declaration -t es2020 --outDir js
bash

輸出:

js/
├── Greet.jsx           // 純 JS
└── Greet.d.ts          // type 宣告

有 .d.ts 的話,以後合作的 TS 開發者還能用 — 變相保留 type 資訊。


實際使用注意

1. JSX 內嵌的 type assertion 會留下
const x = <div>{value as string}</div>;
tsx

as string 在 --jsx preserve 模式下保留在 JSX 中,輸出 .jsx 仍含 TypeScript 語法。 解法:用 --jsx react-jsx 把 JSX 也轉掉(輸出 .js),或先手動清掉 type assertion。

2. import 的 .tsx 副檔名
import Header from './Header';   // OK
import Header from './Header.tsx';   // ❌ 輸出 .jsx 後路徑不對
typescript

寫 import 時 別寫副檔名,讓 tooling 處理。

3. 三斜線指令保留
/// <reference types="node" />
typescript

這類 TS 特有指令在 .jsx 中沒意義,但不會被自動移除。手動刪。


反方向:JSX 轉 TSX

想把舊 JSX 升級成 TSX 反而比較常見(個人 / 團隊 migration):

# 改副檔名
mv App.jsx App.tsx
 
# 安裝 TS + React types
npm install --save-dev typescript @types/react @types/react-dom
 
# 加 tsconfig
npx tsc --init --jsx preserve --target es2020
bash

然後 逐檔加 type,先 any 後修。詳見 TypeScript Migration 官方指南。

目錄