翻譯 | 開始使用 TypeScript 和 React

  • 原文地址:Getting started with TypeScript and React
  • 原文作者:Jack_Franklin
  • 譯者:luxj
  • 校對者:veizz

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

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

更新:如果你想用德語閱讀這篇文章,你可以 thanks to the awesome folks at Reactx.de

安裝配置 TypeScript

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

yarn inityarn add typescript

當你安裝了 TypeScript,你就可以使用 tsc 命令行工具,這個工具可以編譯 TypeScript,編譯時會創建一個開始文件 tsconfig.json,你可以編輯這個文件。你可以運行 tsc --init 獲得這個文件 — 如果你已經在本地安裝了 TypeScript,你需要運行 ./node_modules/.bin/tsc --init。

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

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

{ "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。

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

strict:true

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

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 reactimport ReactDOM from react-domconst 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/reactyarn 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-serverProject is running at http://localhost:3000/webpack output is served from /dist404s will fallback to /index.htmlts-loader: Using typescript@2.3.0 and /Users/jackfranklin/git/interactive-react-introduction/tsconfig.jsonVersion: webpack 2.4.1Time: 6077ms Asset Size Chunks Chunk Namesapp.js 1.14 MB 0 [emitted] [big] mainwebpack: 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:latest 和 tslint-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前端高效開發實戰》已在亞馬遜、京東、噹噹開售。

推薦閱讀:

TypeScript 2.1中的類型運算
是時候再給TypeScript一次機會了【譯】
Typescript會不會借著Angular2,成為主流編程語言?
有哪些公司在使用或者準備使用Angular2?
【認真臉】註解與裝飾器的點點滴滴

TAG:TypeScript | React | webpack |