TypeScript 和 JSX 第一部分 - 什么是 JSX?

2025-06-05

TypeScript 和 JSX 第一部分 - 什么是 JSX?

自 2019 年起,TypeScript 已成为 Web 开发者的首选编程语言,并且越来越受欢迎。在本系列文章中,我们将探讨 TypeScript 编译器如何处理 JSX,以及它如何与最流行的 JSX 使用框架 React 进行交互。

首先,JSX 是如何工作的?以下是一些 JSX 的示例:

// a native 'span' element with some text children
const mySpan = <span>Hello world!</span>

// a custom 'CustomSpan' element with some props and some children
const myCustomSpan = (
  <CustomSpan
    key='_myspan'
    bold
    color="red"
  >
    Hello world!
  </CustomSpan>
)

// a native, self-closing 'input' element without any children
const myInput = <input />

// a custom 'Container' element with multiple children
const myWidget = (
  <Container>
    I am a widget
    <Button>Click me!</Button>
  </Container>
)

JSX 是 JavaScript 的一种非 ECMAScript 兼容语法,TypeScript 通过编译器标志支持它--jsx。如果你是 React 开发者,那么 JSX 实际上只是一种语法糖,它会被编译成这样(如果你使用 TypeScript 编译器):

// a native 'span' element with some text children
const mySpan = React.createElement('span', null, 'Hello world!')

// a custom 'CustomSpan' element with some props and some children
const myCustomSpan = React.createElement(
  CustomSpan,
  { key: 'myspan', bold: true, color: 'red' },
  'Hello world!'
)

// a native, self-closing 'input' element without any children
const myInput = React.createElement('input', null)

// a custom 'Container' element with multiple children
const myWidget = React.createElement(
  Container,
  { onClick: console.log },
  'I am a widget',
  React.createElement(Button, null, 'Click me!')
)

这已经有很多内容需要剖析了;让我们注意一下这个转变中的一些有趣的事情:

  • 整个JSX 表达式变成了对名为 的函数的调用React.createElement。这就是为什么import React from 'react'如果你使用 JSX 就总是需要这样做,即使这个变量React在你的代码中从未真正使用过!
  • JSX 表达式中的标签名称被移动到函数调用的第一个参数。
    • 如果标签名称以大写字母开头,或者(示例中未显示)它是属性访问(如<foo.bar />),则保持原样。
    • 如果标签名称是单个小写单词,则将其转换为字符串文字(input -> 'input'
  • 所有的 props (或者说是属性,在抽象语法树中如此称呼)都会被转换成一个对象,并移动到函数调用的第二个参数,需要注意一些特殊的语法:
    • 如果没有传入任何 props,那么值就不是一个空对象,也不是undefined,而只是null
    • 简写属性语法(如bold中的 prop myInput)被转换为具有值 的对象属性true
    • 尽管 React 将keyref视为特殊属性,但它们仍然(就语法而言)是转换中的常规属性。
    • 对象属性的顺序与它们在 JSX 表达式中作为属性出现的顺序相同。
  • 如果需要的话,子项会被转换(如果它们也是 JSX),并按照它们出现的顺序放置,作为函数调用的其余参数。
    • React 有一种特殊的行为,即 JSX 中的单个子元素在 中仅显示为该节点props.children,但在 中则显示为包含多个子元素的节点数组。语法或规范并未强制要求这样做。实际上,Preact 始终会将子元素包装在一个数组中,无论有多少个,因此这部分属于实现细节。

这就是 JSX 语法的全部内容;归根结底,它只是用于构建嵌套函数调用的语法糖,不会伤到你的大脑。

那么,为什么编译器知道应该使用React.createElement函数而不是其他东西呢?其实你可以把它改成任何你想要的!你只需要在文件顶部添加注释或设置编译器标志:

/* @jsx myCustomJsxFactory.produce */

// your code here
// tsconfig.json
{
  "compilerOptions": { "jsxFactory": "myCustomJsxFactory.produce" }
}

它们做同样的事情,结果只是默认值是React.createElement

在本系列的下一篇文章中,我们将通过构建我们自己的 JSX 工厂函数的工作示例来探索 TypeScript 如何知道如何根据 JSX 语法进行类型检查。

文章来源:https://dev.to/ferdaber/typescript-and-jsx-part-i---what-is-jsx-3g8b
PREV
我花了 20 多年才学到关于开发的这一课
NEXT
[pt-BR]DevRel:关于基础