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前端
第一步要作的事情是使用 Yarn 將 TypeScript 安裝到本地的 node_modules
目錄,首先,使用 yarn init
建立一個工程:java
yarn init yarn add typescript
當你安裝了 TypeScript,你就可使用 tsc
命令行工具,這個工具能夠編譯 TypeScript,編譯時會建立一個開始文件 tsconfig.json
,你能夠編輯這個文件。你能夠運行 tsc --init
得到這個文件 — 若是你已經在本地安裝了 TypeScript
,你須要運行 ./node_modules/.bin/tsc --init
。node
注意:你能夠在個人點開頭的配置文件中看到,我將 $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 處理 ] }
將這個屬性的值設置爲 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
TypeScript 2.3 版本引入了一種新的配置選項,strict
。當將這個值設置爲 true 時,TypeScript 編譯器會盡量的嚴格 - 若是你將一些 JS 轉爲 TS,這可能不是你想要的,可是對於一些新的項目,使其儘量的嚴格是有意義的。它還引入了一些不一樣的配置,其中幾個比較重要的的有 noImplicitAny
和 strictNullChecks
:github
將 TypeScript 引入一個現有的項目,當你不聲明變量的類型時,TypeScript 不會拋出錯誤。可是,當我從零開始新建一個 TypeScript 項目,我但願編譯器儘量地嚴格。
TypeScript 默認作的一件事是將變量設置爲 any
類型。any
是 TypeScript 中避免類型檢查的有效手段,它能夠是任何值。當你轉換 JavaScript 時,使用 any
是頗有用的,可是最好仍是儘量地嚴格。當將 noImplicitAny 設置爲 true,你必須爲變量設置類型。舉個例子,當將 noImplicitAny
設置爲 true 時,下面的代碼會報錯:
function log(thing) { console.log('thing', thing) }
若是你想了解更多關於 noImplicitAny 的信息,能夠閱讀 TypeScript Deep Dive
這是另外一個使 TypeScript 編譯器更嚴格的選項。TypeScript Deep Dive 這本書有一個很好的章節介紹這個選項。若是將這個選項設置爲true,TypeScript 會更容易識別出你引用的一個多是 undefined 值的地方,並將展現這個錯誤。例如:
person.age.increment()
當將 strictNullChecks 設置爲 true,TypeScript 會認爲 person 或者 person.age 多是 undefined,它會報個錯以確保你處理它。這會防止出現運行時錯誤,因此這看起來是一個從一開始就要打開的很棒的選項。
我是 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 應用的準備。
如今,咱們準備好創建 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,咱們只須要運行 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
而且讓每一件事情都如預期同樣運行。
儘管 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前端高效開發實戰》已在亞馬遜、京東、噹噹開售。