TypeScript JSX 编译成什么?一文讲清楚

很多人在用 React + TypeScript 写项目时,都会遇到一个疑问:写在 .tsx 文件里的那些 JSX 代码,最后到底变成了什么?尤其是当项目跑起来后,浏览器根本看不懂 JSX,那它是怎么运行的?

JSX 不是 HTML,更不是最终产物

先说清楚一点:JSX 看起来像 HTML,但它本质上是 JavaScript 的语法扩展。无论是用 TypeScript 还是 JavaScript,JSX 都不能直接被浏览器执行。它必须经过编译,转成标准的 JS 对象或函数调用。

TypeScript 如何处理 JSX

当你写一个 .tsx 文件,比如:

const element = <h1 className="greeting">Hello, TSX</h1>;

TypeScript 编译器(tsc)并不会直接把它变成 DOM 元素。它会根据配置,把这段 JSX 转成特定的函数调用。默认情况下,TypeScript 会把 JSX 编译成 React.createElement() 调用:

const element = React.createElement(
  "h1",
  { className: "greeting" },
  "Hello, TSX"
);

这个过程叫做“JSX 转换”。TypeScript 只负责类型检查和语法转换,真正的编译工作由构建工具(如 Babel、Vite、Webpack)配合插件完成。

编译目标可以自定义

TypeScript 支持通过 jsx 编译选项控制输出行为。常见的有三种模式:

  • react:输出 React.createElement,这是最常用的模式
  • react-jsx:使用新的 JSX 转换,输出 jsx()jsxs() 调用(来自 react/jsx-runtime)
  • preserve:保留 JSX 语法,不作转换,交给后续工具处理

比如,在 tsconfig.json 中设置:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "target": "es2016"
  }
}

这样编译出来的 JSX 就会走新的运行时,减少对顶层 React 引入的依赖,也能优化打包体积。

最终输出的是纯 JavaScript

无论中间过程如何,.tsx 文件最终都会被编译成浏览器能执行的 JavaScript。JSX 部分变成函数调用,TypeScript 类型被擦除,装饰器被降级,整个过程就像把高级配方翻译成机器能读懂的操作指令。

举个实际场景:你在公司写了一个组件,同事拉代码后发现报错,说 React is not defined,明明没改逻辑。问题很可能就是用了 react-jsx 模式但缺少 runtime 引入。这种坑,搞清楚编译原理后就能快速定位。

理解编译过程提升开发效率

知道 TypeScript JSX 最终编译成什么,不只是为了面试答题。当你在调试 React 组件渲染异常,或者优化打包体积时,明白 JSX 是如何变成 createElement 调用的,能帮你更快看懂生成的代码,甚至合理配置 babel 和 tsc 避免重复转换。

比如用 Vite 创建项目时,默认用 esbuild 处理 .ts 和 .tsx,它对 JSX 的支持方式就和传统 Webpack + Babel 不一样。了解底层输出,才能选对配置,避免类型报错或运行时崩溃。