在线文档教程

JSX

JSX

介绍

JSX是一种可嵌入XML的语法。它意味着被转换成有效的JavaScript,尽管该转换的语义是特定于实现的。JSX开始受到React框架的欢迎,但之后也出现了其他应用程序。TypeScript支持嵌入,类型检查和JSX直接编译成JavaScript。

基本用法

为了使用JSX,你必须做两件事。

  • .tsx扩展名命名您的文件

  • 启用jsx选项

TypeScript有三个JSX模式:preservereact,和react-native。这些模式仅影响发射阶段 - 类型检查不受影响。该preserve模式将JSX保留为输出的一部分,以供其他转换步骤(例如Babel)进一步使用。此外,输出将具有.jsx文件扩展名。该react模式将发出React.createElement,在使用之前不需要经过JSX转换,并且输出将具有.js文件扩展名。该react-native模式相当于preserve它保留了所有JSX,但输出将具有.js文件扩展名。

模式输入产量输出文件扩展名
preserve<div /><div />.jsx
react<div />React.createElement( “分区”)JS
react-native<div /><div />JS

您可以使用--jsx命令行标志或tsconfig.json文件中的相应选项指定此模式。

注意:标识符 是硬编码的,所以您必须使React以大写R.React

as操作符

回想一下如何编写一个类型断言:

var foo = <foo>bar;

在这里,我们声明变量bar具有类型foo。由于TypeScript对类型断言也使用尖括号,所以JSX的语法引入了一些解析困难。因此,TypeScript不允许在.tsx文件中使用尖括号类型断言。

为弥补这种.tsx文件功能的损失,增加了一个新的断言操作符:as。上面的例子很容易被as操作员重写。

var foo = bar as foo;

as运营商在这两个可用的.ts.tsx文件,并在行为上的其他类型的断言风格相同。

类型检查

为了理解JSX的类型检查,您必须首先了解内部元素和基于值的元素之间的区别。给定一个JSX表达式<expr />,expr可以引用环境内在的东西(例如,一个div或span一个DOM环境)或者一个你创建的自定义组件。这很重要,原因有两个:

  • 对于React,内部元素以字符串(React.createElement("div"))形式发出,而您创建的组件不是(React.createElement(MyComponent))。

2. 在JSX元素中传递的属性类型应该以不同的方式查找。内在元素属性应该是本质上已知的,而组件可能想要指定它们自己的一组属性。

TypeScript使用与React相同的约定来区分这些约定。内部元素始终以小写字母开头,而基于值的元素始终以大写字母开头。

内在因素

内部元素在特殊界面上查找JSX.IntrinsicElements。默认情况下,如果未指定此接口,则会发生任何情况,并且不会对内部元素进行类型检查。但,如果该接口现在,则内在元素的名称抬头的一个属性JSX.IntrinsicElements界面。例如:

declare namespace JSX { interface IntrinsicElements { foo: any } } <foo />; // ok <bar />; // error

在上面的例子中,<foo />将工作正常,但<bar />会导致一个错误,因为它没有被指定JSX.IntrinsicElements。

注意:你也可以指定一个catch-all字符串索引器,JSX.IntrinsicElements如下所示:declare namespace JSX {interface IntrinsicElements {elemName:string:any; }}

基于价值的元素

基于价值的元素只是由范围内的标识符查找。

import MyComponent from "./myComponent"; <MyComponent />; // ok <SomeOtherComponent />; // error

有两种方法可以定义基于值的元素:

  • 无状态功能组件(SFC)

2. 类组件

由于这两种基于值的元素在JSX表达式中不可区分,我们首先尝试使用重载解析将表达式解析为无状态功能组件。如果这个过程成功了,那么我们就完成了对其声明的表达。如果我们不能作为SFC来解决,我们将尝试作为类组件解决。如果失败了,我们会报告一个错误。

无状态功能组件

顾名思义,该组件被定义为JavaScript函数,其第一个参数是一个props对象。我们强制它的返回类型必须是可赋值的JSX.Element

interface FooProp { name: string; X: number; Y: number; } declare function AnotherComponent(prop: {name: string} function ComponentFoo(prop: FooProp) { return <AnotherComponent name=prop.name />; } const Button = (prop: {value: string}, context: { color: string }) => <button>

因为SFC只是一个JavaScript函数,所以我们也可以在这里使用函数重载。

interface ClickableProps { children: JSX.Element[] | JSX.Element } interface HomeProps extends ClickableProps { home: JSX.Element; } interface SideProps extends ClickableProps { side: JSX.Element | string; } function MainButton(prop: HomeProps): JSX.Element; function MainButton(prop: SideProps): JSX.Element { ... }

类组件

可以限制类组件的类型。但是,为此我们必须引入两个新术语:元素类类型元素实例类型

鉴于<Expr />,元素类的类型是Expr。所以在上面的例子中,如果MyComponent是ES6类,类类型就是那个类。如果MyComponent是工厂函数,那么类的类型就是那个函数。

一旦类类型建立,实例类型由类类型的调用签名和构造签名的返回类型的联合来确定。因此,在ES6类的情况下,实例类型将是该类的一个实例的类型,而在工厂函数的情况下,它将是函数返回值的类型。

class MyComponent { render() {} } // use a construct signature var myComponent = new MyComponent( // element class type => MyComponent // element instance type => { render: () => void } function MyFactoryFunction() { return { render: () => { } } } // use a call signature var myComponent = MyFactoryFunction( // element class type => FactoryFunction // element instance type => { render: () => void }

元素实例类型很有趣,因为它必须是可赋值的,JSX.ElementClass否则会导致错误。默认情况下JSX.ElementClass{},但可以增加它以将JSX的使用限制为仅符合适当界面的那些类型。

declare namespace JSX { interface ElementClass { render: any; } } class MyComponent { render() {} } function MyFactoryFunction() { return { render: () => {} } } <MyComponent />; // ok <MyFactoryFunction />; // ok class NotAValidComponent {} function NotAValidFactoryFunction() { return {}; } <NotAValidComponent />; // error <NotAValidFactoryFunction />; // error

属性类型检查

键入检查属性的第一步是确定元素属性类型。这与内在要素和基于价值的要素略有不同。

对于内在元素,它是属性的类型 JSX.IntrinsicElements

declare namespace JSX { interface IntrinsicElements { foo: { bar?: boolean } } } // element attributes type for 'foo' is '{bar?: boolean}' <foo bar />;

对于基于价值的元素,它有点复杂。它由之前确定的元素实例类型的属性类型确定。决定使用哪个属性JSX.ElementAttributesProperty。它应该用一个属性来声明。然后使用该属性的名称。

declare namespace JSX { interface ElementAttributesProperty { props; // specify the property name to use } } class MyComponent { // specify the property on the element instance type props: { foo?: string; } } // element attributes type for 'MyComponent' is '{foo?: string}' <MyComponent foo="bar" />

元素属性类型用于类型检查JSX中的属性。支持可选和必需的属性。

declare namespace JSX { interface IntrinsicElements { foo: { requiredProp: string; optionalProp?: number } } } <foo requiredProp="bar" />; // ok <foo requiredProp="bar" optionalProp={0} />; // ok <foo />; // error, requiredProp is missing <foo requiredProp={0} />; // error, requiredProp should be a string <foo requiredProp="bar" unknownProp />; // error, unknownProp does not exist <foo requiredProp="bar" some-unknown-prop />; // ok, because 'some-unknown-prop' is not a valid identifier

注意:如果属性名称不是有效的JS标识符(如data-*属性),如果在元素属性类型中找不到它,则不会将其视为错误。

传播运算符也可以工作:

var props = { requiredProp: "bar" }; <foo {...props} />; // ok var badProps = {}; <foo {...badProps} />; // error

子元素类型检查

在2.3中,介绍了子元素的类型检查。子元素元素属性类型中的一个属性,我们通过类型检查属性确定它。与我们如何JSX.ElementAttributesProperty确定道具的名称类似,我们用它JSX.ElementChildrenAttribute来确定子元素的名字。JSX.ElementChildrenAttribute应该用一个属性声明。

declare namespace JSX { interface ElementChildrenAttribute { children: {}; // specify children name to use } }

如果没有明确指定子类型,我们将使用React类型中的默认类型。

<div> <h1>Hello</h1> </div>; <div> <h1>Hello</h1> World </div>; const CustomComp = (props) => <div>props.children</div> <CustomComp> <div>Hello World</div> {"This is just a JS expression..." + 1000} </CustomComp>

您可以指定任何其他属性的类型。这将覆盖来自React类型的默认类型。

interface PropsType { children: JSX.Element name: string } class Component extends React.Component<PropsType, {}> { render() { return ( <h2> this.props.children </h2> ) } } // OK <Component> <h1>Hello World</h1> </Component> // Error: children is of type JSX.Element not array of JSX.Element <Component> <h1>Hello World</h1> <h2>Hello World</h2> </Component> // Error: children is of type JSX.Element not array of JSX.Element or string. <Component> <h1>Hello</h1> World </Component>

JSX结果类型

默认情况下,JSX表达式的结果被键入为any。您可以通过指定JSX.Element接口来自定义类型。但是,无法从此接口检索有关JSX的元素,属性或子级的类型信息。这是一个黑匣子。

嵌入表达式

JSX允许您通过用大括号({ })括起表达式来在表达式之间嵌入表达式。

var a = <div> {["foo", "bar"].map(i => <span>{i / 2}</span>)} </div>

上面的代码会导致错误,因为你不能用一个数字来分割一个字符串。当使用该preserve选项时,输出如下所示:

var a = <div> {["foo", "bar"].map(function (i) { return <span>{i / 2}</span>; })} </div>

反应整合

要将JSX与React一起使用,您应该使用React类型。这些类型定义了JSX适用于React 的名称空间。

/// <reference path="react.d.ts" /> interface Props { foo: string; } class MyComponent extends React.Component<Props, {}> { render() { return <span>{this.props.foo}</span> } } <MyComponent foo="bar" />; // ok <MyComponent foo={0} />; // error