tsx 轉 jsx
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
一條指令轉換
npx tsc --jsx preserve -t es2020 --outDir js --noEmit false
| 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 處理 |
react | React.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
-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"]
}
npx tsc -p tsconfig.export-jsx.json
適合需要重複使用的轉換 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>;
}
// 輸出 JSX
function Greet({ name, age }) {
return <h1>{name} is {age}</h1>;
}
// interface Props 直接消失
tsc 永遠 strip types,沒辦法把 type 留在 JSX 裡。
替代:用 --declaration 同時生 .d.ts
如果你還想保留 type 給未來用:
npx tsc --jsx preserve --declaration -t es2020 --outDir js
輸出:
js/
├── Greet.jsx // 純 JS
└── Greet.d.ts // type 宣告
有 .d.ts 的話,以後合作的 TS 開發者還能用 — 變相保留 type 資訊。
實際使用注意
1. JSX 內嵌的 type assertion 會留下const x = <div>{value as string}</div>;
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 後路徑不對寫 import 時 別寫副檔名,讓 tooling 處理。
3. 三斜線指令保留/// <reference types="node" />這類 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
然後 逐檔加 type,先 any 後修。詳見 TypeScript Migration 官方指南。