TypeScript基礎入門之JSX(一)

轉發 TypeScript基礎入門之JSX(一)react

介紹

JSX是一種可嵌入的相似XML的語法。 它旨在轉換爲有效的JavaScript,儘管該轉換的語義是特定於實現的。 JSX在React框架中愈來愈受歡迎,但此後也看到了其餘實現。 TypeScript支持嵌入,類型檢查和直接編譯JSX到JavaScript。json

基本用法

要使用JSX,您必須作兩件事。 1. 使用.tsx擴展名命名您的文件 2. 啓用jsx選項react-native

TypeScript附帶三種JSX模式:preserve, react 和 react-native。 這些模式僅影響編譯階段 - 類型檢查不受影響。 preserve模式將保持JSX做爲輸出的一部分,以便由另外一個變換步驟(例如Babel)進一步編譯。 此外,輸出將具備.jsx文件擴展名。 react模式將編譯React.createElement,在使用以前不須要通過JSX轉換,輸出將具備.js文件擴展名。 react-native模式至關於保留,由於它保留了全部JSX,但輸出將具備.js文件擴展名。框架

Mode Input Output Output File Extension
preserve     .jsx
react   React.createElement(「div」) .js
react-native     .js

您可使用–jsx命令行標誌或tsconfig.json文件中的相應選項指定此模式。ide

注意:標識符React是硬編碼的,所以必須使用大寫的R使React可用函數

as 操做符

回想一下如何寫一個類型斷言:編碼

var foo = <foo>bar;

聲明變量bar的類型爲foo。 因爲TypeScript還對類型斷言使用尖括號,所以將其與JSX的語法結合會引入某些解析困難。 所以,TypeScript不容許.tsx文件中的尖括號類型斷言。spa

因爲上述語法不能在.tsx文件中使用,所以應使用備用類型斷言運算符:as。 可使用as運算符輕鬆重寫該示例。命令行

var foo = bar as foo;

as運算符在.ts和.tsx文件中都可用,而且行爲與尖括號類型斷言樣式相同。code

 

類型檢查

爲了理解使用JSX進行類型檢查,您必須首先了解內部元素和基於值的元素之間的區別。 給定JSX表達式,expr能夠引用環境固有的東西(例如DOM環境中的div或span)或者您建立的自定義組件。 這有兩個重要緣由:

  1. 對於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

 

在上面的示例中,將正常工做,但將致使錯誤,由於它還沒有在JSX.IntrinsicElements上指定。

注意:您還能夠在JSX.IntrinsicElements上指定catch-all字符串索引器,以下所示:

declare namespace JSX {
   interface IntrinsicElements {
       [elemName: string]: any;
   }
}

基於值的要素

基於值的元素只需經過範圍內的標識符進行查找。

import MyComponent from "./myComponent";

<MyComponent />; // ok
<SomeOtherComponent />; // error

有兩種方法能夠定義基於值的元素:

  1. 無狀態功能組件(SFC)
  2. 類組件

由於這兩種類型的基於值的元素在JSX表達式中沒法區分,因此首先TS嘗試使用重載解析將表達式解析爲無狀態功能組件。 若是該過程成功,則TS完成將表達式解析爲其聲明。 若是該值沒法解析爲SFC,則TS將嘗試將其解析爲類組件。 若是失敗,TS將報告錯誤。

無狀態功能組件

顧名思義,該組件被定義爲JavaScript函數,其第一個參數是props對象。 TS強制其返回類型必須可分配給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的類型。 所以,在上面的示例中,若是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
相關文章
相關標籤/搜索