May 07, 2021 TypeScript
JSX is an embedded XML-like syntax. I t can be converted to a legitimate JavaScript, although the semantics of the conversion are based on different implementations. J SX is popular for react frameworks, but it is also used by other applications. TypeScript supports inline, type checking, and compiling JSX directly into JavaScript.
There are two things you must do to use JSX:
.tsx
extension
jsx
option
TypeScript has two JSX modes:
preserve
react
T
hese patterns only work during the code generation phase - type checking is not affected. J
SX is retained in the build code in
preserve
mode for subsequent conversion operations (e.g.,
Babel).
I
n addition, the output file comes
.jsx
extension.
react
mode generates
React.createElement
which no longer requires conversion before use, and the extension of the output file
.js
Mode | Input | Output | The output file extension |
---|---|---|---|
preserve
|
<div />
|
<div />
|
.jsx
|
react
|
<div />
|
React.createElement("div")
|
.js
|
You can specify patterns by using
--jsx
tag
or the options in tsconfig.json
on the command line.
Note:
React
identifiers are hard code that is written dead, so you must make sure that React (capital R) is available. Note: The identifierReact
is hard-coded, so you must make React available with an uppercase R.
as
operator
Think back to how to write type assertions:
var foo = <foo>bar;
Here we assert that
bar
variable is of
foo
type. B
ecause TypeScript also uses angle brackets to represent type assertions, the syntax of JSX presents difficulties in parsing.
Therefore, TypeScript
.tsx
that use angle brackets in .tsx files.
To compensate
.tsx
a new type assertion symbol has been added:
as
.
The above example can be easily overwrite with the
as
operator:
var foo = bar as foo;
as
operator is
.ts
.tsx
and is equivalent to other types of assertion behavior.
To understand JSX's type check, you must first understand the difference between an inherent element and a value-based element. A
ssuming that there is such a
<expr />
expr
may reference something that come with the environment (for example, div or span in a
div
span
or a component that you customize.
This is important for two reasons:
React.createElement("div")
but the component you customize does
React.createElement(MyComponent)
TypeScript uses the same specifications as React to distinguish them. Inherent elements always begin with a lowercase letter, and value-based elements always begin with a capital letter.
Inherent elements use a special interface
JSX.IntrinsicElements
to find. B
y default, if this interface is not specified, it passes all, with no type checking of the inherent elements. H
owever, if the interface exists, then the name of the inherent element needs
JSX.IntrinsicElements
interface.
For example:
declare namespace JSX {
interface IntrinsicElements {
foo: any
}
}
<foo />; // 正确
<bar />; // 错误
In the example above, there is no problem with the
<foo />
but
<bar />
will report an error because it is
JSX.IntrinsicElements
Note: You can also use
JSX.IntrinsicElements
specify one to capture all string indexes:
declare namespace JSX { interface IntrinsicElements { [elemName: string]: any; } }
Value-based elements are simply found by identifier in the scope in which they are located.
import MyComponent from "./myComponent";
<MyComponent />; // 正确
<SomeOtherComponent />; // 错误
You can limit the type of value-based element. However, in order to do this we need to introduce two new terms: the type of element class and the type of the element instance.
There is
<Expr />
element
class, the type of
Expr
S
o in the example above, if
MyComponent
a class of ES6, then its class type is this class.
If
MyComponent
a factory function, the class type is this function.
Once the class type is established, the instance type is determined, and the return value of the signature is called for the class type and the union type of the construction signature is constructed. Again, in the case of the ES6 class, the instance type is the type of the instance of the class, and in the case of a factory function, the instance type is the value type returned by the function.
class MyComponent {
render() {}
}
// 使用构造签名
var myComponent = new MyComponent();
// 元素类的类型 => MyComponent
// 元素实例的类型 => { render: () => void }
function MyFactoryFunction() {
return {
render: () => {
}
}
}
// 使用调用签名
var myComponent = MyFactoryFunction();
// 元素类的类型 => FactoryFunction
// 元素实例的类型 => { render: () => void }
The instance type of the element is interesting because it must be assigned
JSX.ElementClass
or throw an error. T
he default
JSX.ElementClass
is
{}
can be extended to limit the type of JSX to match the appropriate interface.
declare namespace JSX {
interface ElementClass {
render: any;
}
}
class MyComponent {
render() {}
}
function MyFactoryFunction() {
return { render: () => {} }
}
<MyComponent />; // 正确
<MyFactoryFunction />; // 正确
class NotAValidComponent {}
function NotAValidFactoryFunction() {
return {};
}
<NotAValidComponent />; // 错误
<NotAValidFactoryFunction />; // 错误
The first step in property type checking is to determine the element property type. This is slightly different between intrinsic and value-based elements.
For inherent elements, this is
JSX.IntrinsicElements
property.
declare namespace JSX {
interface IntrinsicElements {
foo: { bar?: boolean }
}
}
// `foo`的元素属性类型为`{bar?: boolean}`
<foo bar />;
For value-based elements, it's a little more complicated. I
t depends on the type of property previously determined on the element instance type. A
s to which property to use to determine the type depends
JSX.ElementAttributesProperty
。 I
t should be defined using a single property.
This property name is then used.
declare namespace JSX {
interface ElementAttributesProperty {
props; // 指定用来使用的属性名
}
}
class MyComponent {
// 在元素实例类型上指定属性
props: {
foo?: string;
}
}
// `MyComponent`的元素属性类型为`{foo?: string}`
<MyComponent foo="bar" />
The type check of the property is performed in JSX for the element property type. Optional and required properties are supported.
declare namespace JSX {
interface IntrinsicElements {
foo: { requiredProp: string; optionalProp?: number }
}
}
<foo requiredProp="bar" />; // 正确
<foo requiredProp="bar" optionalProp={0} />; // 正确
<foo />; // 错误, 缺少 requiredProp
<foo requiredProp={0} />; // 错误, requiredProp 应该是字符串
<foo requiredProp="bar" unknownProp />; // 错误, unknownProp 不存在
<foo requiredProp="bar" some-unknown-prop />; // 正确, `some-unknown-prop`不是个合法的标识符
Note: If a property name is not a legitimate JS identifier
data-*
property) and it does not appear in the element property type, it is not treated as an error.
The extension operator can also be used:
var props = { requiredProp: 'bar' };
<foo {...props} />; // 正确
var badProps = {};
<foo {...badProps} />; // 错误
By default, the type of JSX expression result is
any。 你可以自定义这个类型,通过指定
JSX. E
lement'interface. H
owever, you can't retrieve type information about elements, properties, or JSX's child elements from the interface.
It is a black box.
JSX allows you to
{ }
using the hashtag.
var a = <div>
{['foo', 'bar'].map(i => <span>{i / 2}</span>)}
</div>
The above code produces an error because you can't divide a number by a string.
The output is as follows, if you use
preserve
option:
var a = <div>
{['foo', 'bar'].map(function (i) { return <span>{i / 2}</span>; })}
</div>
To work with JSX and React together, you should define
using the React type.
These type declarations define
JSX
appropriate namespace to use 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" />; // 正确
<MyComponent foo={0} />; // 错误