1. 程式人生 > >TypeScript基礎入門之JSX(二)

TypeScript基礎入門之JSX(二)

屬性型別檢查

鍵入檢查屬性的第一步是確定元素屬性型別。 內在元素和基於價值的元素之間略有不同。

對於內部元素,它是JSX.IntrinsicElements上的屬性型別

declare namespace JSX {
  interface IntrinsicElements {
    foo: { bar?: boolean }
  }
}

// element attributes type for 'foo' is '{bar?: boolean}'
<foo bar />;

對於基於價值的元素,它有點複雜。 它由先前確定的元素例項型別上的屬性型別確定。 使用哪個屬性由JSX.ElementAttributesProperty確定。 它應該用單個屬性宣告。 然後使用該屬性的名稱。 從TypeScript 2.8開始,如果未提供JSX.ElementAttributesProperty,則將使用類元素的建構函式或SFC呼叫的第一個引數的型別。

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-*屬性),則如果在元素屬性型別中找不到它,則不會將其視為錯誤。

此外,JSX.IntrinsicAttributes介面可用於指定JSX框架使用的額外屬性,這些屬性通常不被元件的props或引數使用 - 例如React中的鍵。 進一步說,通用JSX.IntrinsicClassAttributes <T>型別也可用於為類元件(而不是SFC)指定相同型別的額外屬性。 在此型別中,泛型引數對應於類例項型別。 在React中,這用於允許型別為Ref <T>的ref屬性。 一般來說,這些介面上的所有屬性都應該是可選的,除非您打算讓JSX框架的使用者需要在每個標記上提供一些屬性。 擴充套件操作符也是有效的:

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

var badProps = {};
<foo {...badProps} />; // error

子型別檢查

在TypeScript 2.3中,TS引入了子型別檢查。 children是元素屬性型別中的特殊屬性,其中子JSXExpressions被插入到屬性中。 類似於TS使用JSX.ElementAttributesProperty來確定props的名稱,TS使用JSX.ElementChildrenAttribute來確定這些props中的子項名稱。 應使用單個屬性宣告JSX.ElementChildrenAttribute。

declare namespace JSX {
  interface ElementChildrenAttribute {
    children: {};  // specify children name to use
  }
}
<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 typings(如果您使用它們)。

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>

React整合

要將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

工廠函式

jsx:react編譯器選項使用的確切工廠函式是可配置的。 可以使用jsxFactory命令列選項或內聯@jsx註釋編譯指示來設定它以基於每個檔案進行設定。 例如,如果將jsxFactory設定為createElement,則<div />將作為createElement("div")而不是React.createElement("div")來編譯。

註釋pragma版本可以像這樣使用(在TypeScript 2.8中):

import preact = require("preact");
/* @jsx preact.h */
const x = <div />;

編譯為

const preact = require("preact");
const x = preact.h("div", null);

選擇的工廠也將影響JSX名稱空間的查詢位置(用於型別檢查資訊),然後再回退到全域性名稱空間。 如果工廠定義為React.createElement(預設值),編譯器將在檢查全域性JSX之前檢查React.JSX。 如果工廠定義為h,它將在全域性JSX之前檢查h.JSX。