翻譯 | 開始使用 TypeScript 和 React

Tom Dale 和其餘人有一些關於 TypeScript 比較好的博文,跟隨這些博文,我最近開始使用 TypeScript。今天,我將展現如何從零開始創建一個 TypeScript 工程,以及如何使用 Webpack 管理構建過程。我也將陳述關於 TypeScript 的第一印象,尤爲是使用 TypeScript 和 ReactJS。javascript

我不會深刻到 TypeScript 語法的具體細節,你能夠閱讀 TypeScript handbook 或者免費書籍 TypeScript Deep Dive,它們是關於 TypeScript 比較好的入門材料。html

更新:若是你想用德語閱讀這篇文章,你能夠 thanks to the awesome folks at Reactx.de前端

安裝配置 TypeScript

第一步要作的事情是使用 Yarn 將 TypeScript 安裝到本地的 node_modules 目錄,首先,使用 yarn init 建立一個工程:java

yarn init
yarn add typescript複製代碼

當你安裝了 TypeScript,你就可使用 tsc 命令行工具,這個工具能夠編譯 TypeScript,編譯時會建立一個開始文件 tsconfig.json,你能夠編輯這個文件。你能夠運行 tsc --init 得到這個文件 — 若是你已經在本地安裝了 TypeScript,你須要運行 ./node_modules/.bin/tsc --initnode

注意:你能夠在個人點開頭的配置文件中看到,我將 $PATH 定義爲 ./node_modules/.bin 這個目錄。這有點危險,由於我可能不經意地運行這個目錄下的任何可執行的文件,可是我願意承擔這個風險,由於我知道這個目錄下安裝了什麼,並且這能節省不少打字時間!react

tsc --init 這個命令會生成一個 tsconfig.json 文件,全部 TypeScript 編譯器的配置都存在於這個文件中。在默認配置的基礎上,我作了一些修改,下面是我正在用的一個配置:webpack

{
  "compilerOptions": {
    "module": "es6", // 使用 ES2015 模塊
    "target": "es6", // 編譯成 ES2015 (Babel 將作剩下的事情)
    "allowSyntheticDefaultImports": true, // 看下面
    "baseUrl": "src", // 能夠相對這個目錄 import 文件
    "sourceMap": true, // 使 TypeScript 生成 sourcemaps
    "outDir": "ts-build", // 構建輸出目錄 (由於咱們大部分時間都在使用 Webpack,因此不太相關)
    "jsx": "preserve", // 開啓 JSX 模式, 可是 "preserve" 告訴 TypeScript 不要轉換它(咱們將使用 Babel)
    "strict": true,
  },
  "exclude": [
    "node_modules" // 這個目錄下的代碼不會被 typescript 處理
  ]
}複製代碼

allowSyntheticDefaultImports

將這個屬性的值設置爲 true,它容許你使用 ES2015 默認的 imports 風格, 即便你導入的代碼沒有使用 ES2015 默認的 export。git

舉個例子,當你 import 不是用 ES2015 編寫的 React 時(雖然源碼是,可是 React 使用一個構建好的版本),就能夠利用上面的屬性設置。這意味着,嚴格意義上來說,它沒有使用 ES2015 默認的 export,因此當你使用 import 的時候, TypeScript 會警告你。儘管如此,像 Webpack 這樣的構建工具可以導入正確的代碼,因此我將這個屬性設置爲 true,相比使用 import * as React from 'react',我更喜歡 import React from 'react' 這種方式。es6

strict:true

TypeScript 2.3 版本引入了一種新的配置選項,strict。當將這個值設置爲 true 時,TypeScript 編譯器會盡量的嚴格 - 若是你將一些 JS 轉爲 TS,這可能不是你想要的,可是對於一些新的項目,使其儘量的嚴格是有意義的。它還引入了一些不一樣的配置,其中幾個比較重要的的有 noImplicitAnystrictNullChecks:github

noImplicitAny

將 TypeScript 引入一個現有的項目,當你不聲明變量的類型時,TypeScript 不會拋出錯誤。可是,當我從零開始新建一個 TypeScript 項目,我但願編譯器儘量地嚴格。
TypeScript 默認作的一件事是將變量設置爲 any 類型。any 是 TypeScript 中避免類型檢查的有效手段,它能夠是任何值。當你轉換 JavaScript 時,使用 any 是頗有用的,可是最好仍是儘量地嚴格。當將 noImplicitAny 設置爲 true,你必須爲變量設置類型。舉個例子,當將 noImplicitAny 設置爲 true 時,下面的代碼會報錯:

function log(thing) {
  console.log('thing', thing)
}複製代碼

若是你想了解更多關於 noImplicitAny 的信息,能夠閱讀 TypeScript Deep Dive

strictNullChecks

這是另外一個使 TypeScript 編譯器更嚴格的選項。TypeScript Deep Dive 這本書有一個很好的章節介紹這個選項。若是將這個選項設置爲true,TypeScript 會更容易識別出你引用的一個多是 undefined 值的地方,並將展現這個錯誤。例如:

person.age.increment()複製代碼

當將 strictNullChecks 設置爲 true,TypeScript 會認爲 person 或者 person.age 多是 undefined,它會報個錯以確保你處理它。這會防止出現運行時錯誤,因此這看起來是一個從一開始就要打開的很棒的選項。

配置 Webpack, Babel and TypeScript

我是 Webpack 的腦殘粉;我喜歡它的插件生態系統、開發者工做流,喜歡它擅長管理複雜的應用和構建流程。因此,即便咱們可能僅僅使用 TypeScript 編譯器,我仍然喜歡引入 Webpack。由於 TypeScript 輸出 React 和 es6(也就是 es2015,babel 把 es6 轉成 es5,因此咱們還須要 babel。讓咱們安裝 Webpack,Babel 和相關的 presets 及 ts-loader,ts-loader 是 TypeScript 在 Webpack 中的插件。

還有 awesome-typescript-loader ,也是 TypeScript 在 Webpack 中的插件,可是我首先找到的是 ts-loader 並且到目前爲止它很是不錯。若是誰使用了 awesome-typescript-loader,我很樂意看到關於它們二者的對比。

yarn add webpack babel-core babel-loader babel-preset-es2015 babel-preset-react ts-loader webpack-dev-server複製代碼

此時此刻,我必須感謝 Tom Duncalf,他在博客中發表的 TypeScript 1.9 + React,對我來講,是一個特別好的開始,我極力推薦它。

在 Webpack 中沒有特別的配置,可是我仍是在代碼中列出一些註釋來解釋它:

const webpack = require('webpack')
const path = require('path')

module.exports = {
  // 設置 sourcemaps 爲 eval 模式,將模塊封裝到 eval 包裹起來
  devtool: 'eval',

  // 咱們應用的入口, 在 `src` 目錄 (咱們添加到下面的 resolve.modules):
  entry: [
    'index.tsx'
  ],

  // 配置 devServer 的輸出目錄和 publicPath
  output: {
    filename: 'app.js',
    publicPath: 'dist',
    path: path.resolve('dist')
  },

  // 配置 devServer 
  devServer: {
    port: 3000,
    historyApiFallback: true,
    inline: true,
  },

  // 告訴 Webpack 加載 TypeScript 文件
  resolve: {
    // 首先尋找模塊中的 .ts(x) 文件, 而後是 .js 文件
    extensions: ['.ts', '.tsx', '.js'],

    // 在模塊中添加 src, 當你導入文件時,能夠將 src 做爲相關路徑
    modules: ['src', 'node_modules'],
  },

  module: {
    loaders: [
      // .ts(x) 文件應該首先通過 Typescript loader 的處理, 而後是 babel 的處理
      { test: /\.tsx?$/, loaders: ['babel-loader', 'ts-loader'], include: path.resolve('src') }
    ]
  },
}複製代碼

咱們按照上面的方式配置 loaders ,從而使 .ts(x) 文件首先通過 ts-loader 的處理。按照 tsconfig.json 中的配置,使用 TypeScript 編譯 .ts(x) 文件 - 輸出 ES2015。而後,咱們使用 Babel 將它降級到 ES5。爲了實現這些,我建立了一個包含須要的 presets 的 .babelrc 文件:

{
  "presets": ["es2015", "react"]
}複製代碼

如今咱們已經作好了寫 TypeScript 應用的準備。

寫一個 TypeScript React 組件

如今,咱們準備好創建 src/index.tsx,這是咱們這個應用的入口。咱們能夠建立一個虛擬的組件,渲染它,查看它是否正常運行。

import React from 'react'
import ReactDOM from 'react-dom'

const App = () => {
  return (
    <div> <p>Hello world!</p> </div>
  )
}

ReactDOM.render(<App />, document.getElementById('app'))複製代碼

若是你運行 webpack,會看到下面的錯誤:

ERROR in ./src/index.tsx
(1,19): error TS2307: Cannot find module 'react'.

ERROR in ./src/index.tsx
(2,22): error TS2307: Cannot find module 'react-dom'.複製代碼

發生上面的錯誤是由於 TypeScript 試圖確認 React 的類型、React 導出了什麼。對於 React DOM,TypeScript 會作一樣的事情。React 並非使用 TypeScript 編寫的,因此它並無包含那些信息。幸運地是,爲了應對這種狀況,社區已經建立了 DefinitelyTyped,這是一個大型的組件類型庫。

最近,安裝機制改變了;全部的類型被髮布到 npm @types scope 下。爲了得到 React 和 ReactDOM 的類型,咱們運行下面的命令:

yarn add @types/react
yarn add @types/react-dom複製代碼

經過上面的處理,錯誤不見了。不管什麼時候,你安裝一個依賴時,都應該試着安裝 @types 包,或者你想查看是否有被支持的類型,你能夠在 TypeSearch 網站上查看。

本地運行 app

爲了在本地運行 app,咱們只須要運行 webpack-dev-server 命令。我配置了一個腳本 start, 它能作上面的事情:

"scripts": {
  "start": "webpack-dev-server"
}複製代碼

服務會找到 webpack.config.json 這個文件,使用它建立咱們的應用。

若是你運行 yarn start ,你會看到來自於 webpack-dev-server 的輸出,包含 ts-loader 的輸出,這些可以確認應用是否正常運行。

$ webpack-dev-server
Project is running at http://localhost:3000/
webpack output is served from /dist
404s will fallback to /index.html
ts-loader: Using typescript@2.3.0 and /Users/jackfranklin/git/interactive-react-introduction/tsconfig.json
Version: webpack 2.4.1
Time: 6077ms
 Asset     Size  Chunks                    Chunk Names
app.js  1.14 MB       0  [emitted]  [big]  main
webpack: Compiled successfully.複製代碼

爲了可以在本地看到效果,我建立了一個 index.html 文件,讓它加載編譯後的代碼:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>My Typescript App</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="dist/app.js"></script>
  </body>複製代碼

在端口 3000,咱們將會看到 Hello world!,咱們讓 TypeScript 運行了!

定義一個模塊類型

在如今的工程中,我想使用 React Ace module 包含一個代碼編輯器。可是這個模塊並不提供 types,而且也沒有 @types/react-ace。在這種狀況下,咱們必須在應用中增長類型,這樣可使 TypeScript 知道如何去檢查它的類型。這看起來很是煩人,讓 TypeScript 至少知道全部第三方依賴關係的好處是,能夠節省調試時間。

定義一個只包含類型的文件,後綴是 .d.ts( ‘d‘ 表明 ‘declaration‘ ),你能夠從 TypeScript docs 瞭解更多。在你的工程中,TypeScript 將會自動地找到這些文件,你不須要顯式地導入它們。

我建立了 react-ace.d.ts 文件,添加下面的代碼,建立模塊,定義它的默認 export 爲一個 React 組件。

declare module 'react-ace' {
    interface ReactAceProps {
      mode: string
      theme: string
      name: string
      editorProps?: {}
      showPrintMargin?: boolean
      minLines?: number
      maxLines?: number
      wrapEnabled?: boolean
      value: string
      highlightActiveLine?: boolean
      width?: string
      fontSize?: number
    }

    const ReactAce: React.ComponentClass<ReactAceProps>
    export = ReactAce
}複製代碼

我首先建立了一個 TypeScript 接口,這個接口包含組件的屬性,export = ReactAce 標明組件經過模塊被導出。經過定義屬性的類型,TypeScript 會告訴我是否弄錯了屬性的類型或者忘記設置一個類型,這是很是有價值的。

測試

最後,使用 TypeScript,我也想有一個很好的測試方案。我是 Facebook 的 Jest 的超級粉絲,我在 google 上作了一些搜索,確認它是否能用 TypeScript 運行。結果發現,這是可行的,ts-jest 包能夠作一些很重的轉換。除此以外,還有一個作類型檢查的 @types/jest 包,可讓你全部的測試都是類型確認過的。

很是感謝 RJ Zaworski,他在博客上發表了 TypeScript and Jest ,這使我開始瞭解這個主題。若是你安裝了 ts-jest,你只須要在 package.json 中配置 Jest,下面是配置:

"jest": {
  "moduleFileExtensions": [
    "ts",
    "tsx",
    "js"
  ],
  "transform": {
    "\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
  },
  "testRegex": "/*.spec.(ts|tsx|js)$"
},複製代碼

第一個配置告訴 Jest 尋找 .ts.tsx 文件。 transform 對象告訴 Jest 經過 ts-jest 預處理器運行任何 TypeScript 文件,ts-jest 預處理器經過 TypeScript 編譯器運行 TypeScript 文件,產出能讓 Jest 識別的 JavaScript。最後,我更新了 testRegex 設置,目的是尋找任何 *.spec.ts(x) 文件,我更喜歡用這種方式命名轉換。

經過上面這些配置,我能夠運行 jest 而且讓每一件事情都如預期同樣運行。

使用 TSLint 規範代碼

儘管 TypeScript 在代碼中會給出不少檢查提示,我仍然想要一個規範器,作些代碼風格和質量檢查。就像 JavaScript 的 ESLint,TSLint 是檢查 TypeScript 的最好選擇。它和 ESlint 的工做方式相同 - 用一系列生效或不生效的規則,還有一個 TSLint-React 包增長 React 的具體規則。

你能夠經過 tslint.json 文件配置 TSLint,個人配置文件以下。我用 tslint:latesttslint-react presets,它們可使用不少規則。我不同意一些默認設置,因此我重寫了它們 - 你能夠和個人配置不一樣 - 這徹底取決於你!

{
    "defaultSeverity": "error",
    "extends": ["tslint:latest", "tslint-react"],
    "jsRules": {},
    "rules": {
      // 用單引號, 可是在 JSX 中,強制使用雙引號
      "quotemark": [true, "single", "jsx-double"],
      // 我更喜歡沒有分號 :)
      "semicolon": [true, "never"],
      // 這個規則使每一個接口以 I 開頭,這點我不喜歡
      "interface-name": [true, "never-prefix"],
      // 這個規則強制對象中的 key 按照字母順序排列 
      "object-literal-sort-keys": false
    },
    "rulesDirectory": []
}複製代碼

我能夠運行 tslint --project tsconfig.json 規範個人項目

結論

總之,到目前爲止,用 TypeScript 開發我很高興。我確定會發表更多博文來描述這門語言的細節和我是如何使用 TypeScript 的。但僅就以下操做而言,構建過程、配置全部的工具、開始使用類型,這真是一種享受。若是你正在將你的 JS 應用結構化,想要一個更強大的編譯器避免錯誤並減小調試時間,我極力推薦你嘗試 TypeScript。

若是你想看源碼或者以本文中的例子做爲開始,我在 GitHub 上放了一個例子,若是你有任何問題,能夠提 issue。

iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。

相關文章
相關標籤/搜索