Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

TypeScript JSX


May 07, 2021 TypeScript


Table of contents


Introduction to TypeScript JSX

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.

Basic usage

There are two things you must do to use JSX:

  1. Give the file a .tsx extension
  2. Enable 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 identifier React 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.

Type check

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:

  1. For React, the intrinsic element generates a string React.createElement("div") but the component you customize does React.createElement(MyComponent)
  2. Property types passed in into JSX elements are found differently. Inherent element properties themselves are supported, but custom components specify which properties they have.

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

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

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 />; // 错误

Property type check

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} />; // 错误

JSX result type

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.

The embedded expression

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>

React integration

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} />; // 错误