【譯】結合使用 TypeScript 與 React

原文:Using TypeScript with React
做者:Simon Knott
譯者:不肥的肥羊
爲保證文章的可讀性,本文采用意譯
閱讀全文約 13 分鐘html

這篇文章是基於我過去在 React JS & React Native Bonn Meetup 上所作的演講。文章的目的是回答如下幾個問題:前端

  • 什麼是 TypeScript?
  • 如何在 React 中使用 TypeScript?
  • 爲何咱們(不)應該使用 TypeScript?

這是我在學校以外的第一次公開演講,可以順利結束真是極好的,謝謝全部前來捧場的朋友!😁演講是我很是喜歡的一件事情,但願將來能作更多的演講。react

什麼是 TypeScript?

typescriptlang.org 對 TypeScript(縮寫爲 TS)有以下解釋:git

TypeScript 是 JavaScript 的類型超集,能夠編譯爲純 JavaScript。github

上面這段描述能夠解釋爲:typescript

  1. TypeScript 是一種與 JavaScript 以某種形式關聯的類型語言。它們主要的區別就在於,TypeScript 是一種靜態類型語言,而 JavaScript 則是動態類型的。
  2. TypeScript 能夠被編譯爲純 JavaScript,而後在諸如瀏覽器或 Node.js 等環境中執行。
  3. 做爲 JavaScript 的超集,意味着 TypeScript 只會在 JavaScript 的基礎上添加新特性,同時仍與底層 JS 規範保持嚴格的兼容性。

讓咱們看一個栗子,因爲 TS 是 JS 的超集,所以如下的代碼對兩種語言都是有效的:編程

function add(a, b) {
  return a + b;
}

const favNumber = add(31, 11);
複製代碼

咱們已經瞭解到,TS 的特殊性在於它的類型系統。那麼咱們爲以上的代碼添加類型聲明,就會產生下面符合 TS 規範,卻不符合 JS 規範的代碼:react-native

function add(a: number, b: number): number {
  return a + b;
}

const favNumber: number = add(31, 11);
複製代碼

經過添加這些註解,TypeScript 編譯器能夠檢查你的代碼以查找出一些代碼層面的 bug。例如,它可能會發現不匹配的參數類型,或者做用域外的調用等等。數組

使用 tsc 命令行工具編譯 TypeScript 會經歷:對代碼進行類型檢查 -> 全部檢查都經過 -> 輸出純 JavaScript。輸出的代碼與源代碼很類似,只是沒有類型註解。瀏覽器

TypeScript 還支持基本類型推斷,它經過省略函數返回值的類型以及大多數狀況下的變量賦值類型來簡化代碼:

function add(a: number, b: number) {
  return a + b;
}

const favNumber = add(31, 11);
複製代碼

(與上一段代碼的區別是,函數返回值沒有類型註解。)

TS 中有效的基本類型有 booleanstringnumberSymbolnullundefinedBigInt 類型;還有 void 類型,用於表示函數不返回任何內容;還有 Function 類型,以及,一般用 Array<T> 來表示 string[]number[] 這樣須要註解數組內容類型的變量。還有像 ReactElementHTMLInputElement 以及 Express.App 這些在特定的部署環境或者有特定的依賴時纔有效的類型。

TS 中最有趣的事情是你能夠定義本身的類型。讓咱們看看一些爲你的領域建模的方式。首先是使用 interface 關鍵字定義你的對象:

interface User {
  firstName: string;
  lastName: string;
  age: number;
  state: UserState;
}
複製代碼

你還能夠定義 enums

enum UserState {
  ACTIVE,
  INACTIVE,
  INVITED,
}
複製代碼

TypeScript 甚至支持繼承:

interface FunkyUser extends User {
  isDancing: boolean;
}
複製代碼

TypeScript 還有不少高級類型,好比聯合類型,它能夠用來代替枚舉類型,它們的區別是,它更像 JavaScript 的書寫方式。

type UserState =
  "active" |
  "inactive" |
  "invited";

const correctState: UserState = "active"; // ✅
const incorrectState: UserState = "loggedin"; // ❌ TypeError
複製代碼

在類型檢查的時候,TypeScript 不會檢查原型鏈或其餘繼承的方法,它只檢查屬性的類型標識以及處理對象的特性:

const bugs: User = {
  firstName: "Bugs",
  lastName: "Bunny",
  age: "too old", // TypeError: expected `number` but got `string`
  state: UserState.ACTIVE,
}
複製代碼

你固然能夠在 TypeScript 中使用 class,我並無讓你遠離它,但要注意:我會在後文解釋,爲何你不須要它。看下面這個栗子:

class JavaLover implements User {
  private firstName: string;
  private lastName: string;
  private age: number;
  private state: UserState;
  
  getOpinion() {
    return [ "!!JAVA!1!" ];
  }
}
複製代碼

前面咱們看了一些基礎的 TypeScript 代碼,對於它作了什麼有了一個大體的瞭解,如今讓咱們來看看它在 React 中是如何使用的。

如何在 React 中使用 TypeScript?

因爲 Babel 7 支持了 TypeScript 的內建,把它整合到你的構建流程變得十分簡單。我仍然建議你查看構建工具的文檔,其中大部分都包含有關 TypeScript 設置,寫的很好的操做指南。

配置好構建流程以後,你就能夠在你的組件中使用 TypeScript 了。下面是一個簡單的 React-Native 組件,它接收一個 TodoItem 和一個回調函數,並展現了一條 Todo 信息。

import * as React from "react";
import { View, Text, CheckBox } from "react-native";

interface TodoItem {
  id: string;
  name: string;
  isCompleted: boolean;
}

interface TodoListItemProps {
  item: TodoItem;
  onComplete: (id: string) => void;
}

function TodoListItem(props: TodoListItemProps) {
  const { item, onComplete } = props;
  return (
    <View> <Text>{item.name}</Text> <CheckBox isChecked={item.isCompleted} onClick={state => { onComplete(item.id); }} /> </View> ); } 複製代碼

因爲 React 本質上是純 JavaScript,所以能夠像純 JavaScript 同樣編寫。你只須要使用 interface(見 TodoListItemPropsinterface 定義)來定義你組件的 props 結構,並使用剛剛定義的 TodoListItemProps 類型來定義組件 props 參數的類型。你無需指定返回類型(事實上它的類型是 JSX.Element),由於能夠從組件返回的表達式的類型中推斷出來。

即使 JSX 不是 JavaScript 規範的一部分,TypeScript 仍然能夠對它進行類型檢查,這使它能夠校驗傳入組件中的 props。

你還能夠將 TypeScript 與 React 的 class API 結合使用:

import * as React from "react";

interface TimerState {
  count: number;
}

class Timer extends React.Component<{}, TimerState> {
  
  state: TimerState = {
    count: 0
  }

  timerId: number | undefined = undefined;

  componentDidMount() {
    this.timerId = setInterval(
      () => {
        this.setState(
          state => ({
            count: state.count + 1
          })
        );
      },
      1000
    );
  }

  componentWillUnmount() {
    if (this.timerId) {
      clearInterval(this.timerId);
    }
  }

  render() {
    return (
      <p>Count: {this.state.count}</p>
    )
  }
}
複製代碼

在你編寫一個 class 時,你會將類型參數傳入到你 extend 出來的 React.Component 中。首先是前面示例組件中爲空的 props({} 空對象);第二個通用類型參數是組件的 state,在這裏是一個只包含數字類型的 count 字段。你還會發現這裏使用了初始化爲 undefined 的實例字段 timerId,這也是爲何它的類型被聲明爲 number | undefined,這表示它能夠是 numberundefined 類型。

如你所見,將 TypeScript 與 React 結合使用很是簡單,不須要任何儀式。基本上,它是 prop-types 更強大的替代品,由於它支持更高級的類型,而且也可使用普通的 JS 代碼。同時,TypeScript 能夠提供編譯時的校驗,而 prop-types 只在開發環境中有效。

若是您想本身搗鼓一些代碼,能夠看看我在現場演示中使用的示例。它就在這個 repo 的 todo 文件夾中:github.com/Skn0tt/reac…

爲何咱們(不)應該使用 TypeScript?

到目前爲止,我假設您對 TypeScript 是什麼以及它能夠作什麼有一個大體的瞭解。那麼接下來我會繼續說明,若是咱們不使用 TypeScript,那是由於什麼。

不使用 TypeScript 的緣由

TypeScript 並非編寫 JavaScript 的全新方式,它只是擴展了 JavaScript 編寫類型註解的能力。首先,最重要的是,TypeScript 是一個類型檢查器,但你們彷佛有忽略這個事實的趨勢,就像我最近看到的一些言論:

「TypeScript 太棒了,終究咱們的 Java 開發工做也能在前端環境下進行了。」

這看起來頗有意義,但其實是有害的。沒錯,Java 開發者已經習慣了類型系統、class、interface 等優秀特性,但 TypeScript 對他們來講依然很是有吸引力。然而,若是 Java(或其餘受面向對象編程影響較深的語言)開發者轉到編寫 TypeScript,又不瞭解 Java 與 JavaScript 之間根深蒂固的差別,那也許會帶來很大的問題。記住一點:不管你的 JavaScript 代碼是否由 TypeScript 編譯而來,都會改變其運行時的行爲。它一旦運行,就再也不會有類型信息,這是它的優點,也是它的痛點。JavaScript 經過原型鏈繼承實現了動態類型和強制類型的功能,這讓它擁有了許多新的功能和特性,距離 Java 也不遠了。在從 Java 開發轉換到 TypeScript 開發時要一直記住這一點。

TypeScript 爲什麼仍能幫助我,如何作到?

在項目中添加類型註解是代碼結構文檔化的極佳方式,它提供了一種爲傳遞的數據註明結構的方式。TypeScript 仍是「機器可讀」的,這意味着它能夠由機器執行!這樣就能夠提供強大的編輯器支持(嘗試用用 VS Code 吧,它 diao bao le!),這簡化了代碼重構的過程,並加強了靜態的安全檢查。

代碼結構文檔化這件事,TypeScript 不只是容許你去作,而是強制你去作。會讓你停下來複盤你正在編寫的代碼,並思考是否能建立更清晰的代碼結構以得到更高的代碼質量。

至少對我來講,編寫類型註解讓個人代碼更利於理解,由於我不須要深刻研究項目就能夠了解數據的結構,它消除了不少認知上的複雜性。

相關文章
相關標籤/搜索