創建的代碼規範沒人遵照,項目中遍地風格迥異的代碼,你會不會抓狂?javascript
經過測試用例的程序還會出現Bug,而緣由僅僅是本身犯下的低級錯誤,你會不會抓狂?前端
某種代碼寫法存在問題致使崩潰時,只能全工程檢查代碼,這須要人工花費大量時間Review代碼,你會不會抓狂?java
以上這些問題,能夠經過靜態檢查有效地緩解!node
靜態檢查(Static Program Analysis)主要是以不運行程序的方式對於程序源代碼進行檢查分析的技術,而與之相反的就是動態檢查(Dynamic Program Analysis),經過實際運行程序輸入測試數據產生預期結果的技術。經過代碼靜態檢查,咱們能夠快速定位代碼的錯誤與缺陷,能夠減小逐行閱讀代碼浪費的時間,能夠(根據須要)快速掃描代碼中可能存在的漏洞等。代碼靜態檢查能夠在代碼的規範性、安全性、可靠性、可維護性等方面起到重要做用。react
在客戶端中,Android可使用CheckStyle、Lint、Findbugs、PMD等工具,iOS可使用Clang Static Analyzer、OCLint等工具。而在React Native的開發過程當中,針對於JavaScript的ESLint,與TypeScript的TSLint,則成爲了主要代碼靜態檢查的工具。本文將按照使用TSLint的緣由、使用TSLint的方法、自定義TSLint的步驟進行探究分析。git
在客戶端團隊進入React Native項目的開發過程當中,面臨着以下問題:github
雖然以上問題能夠經過屢次不斷將雷點標記出,並不斷地分享經驗與強化代碼Review過程等方式來進行緩解,可是仍面臨着React Native開發者掌握的技術水平千差萬別,知識分享傳播的速度緩慢等問題,既致使了開發成本的不斷增長和開發效率持續低下的問題,還難以免一個坑被踩了屢次的狀況出現。這時急需一款能夠知足如下目標的工具:typescript
根據上述要求的描述,靜態檢查工具TSLint能夠較爲有效地達成目標。npm
TSLint是硅谷企業Palantir的一個項目,它是一款能夠檢查TypeScript代碼可讀性、可維護性以及功能性錯誤的靜態檢查工具,當前許多編輯器(Editors)和構建系統(Build Systems)支持這一工具,同時支持自定義編寫Lint規則、配置、格式化等。json
當前TSLint已經包含了上百條規則,這些規則構築了當前TSLint檢查的基礎。在代碼開發階段中,經過這些配置好的規則能夠給工程一個完整的檢查,並隨時能夠提示出可能存在的問題。本文內容參考了TSLint官方文檔palantir.github.io/tslint/。
如下規則主要來源於TSLint規則,是某些規則的簡單介紹。
上述2.1所列出的規則來源於Palantir官方TSLint規則。實際還有多種,可能會用到的有如下:
咱們在項目的規則配置過程當中,通常採用上述規則包其中一種或者若干種同時配置,那如何配置呢?請看下文。
首先,在工程package.json文件中配置TSLint包:
在根目錄中的tslint.json文件中能夠根據須要配置已有規則,例如:
其中extends數組內放置繼承的TSLint規則包,上圖包括了airbnb配置的規則包、tslint-react的規則包,而rules用於配置規則的開關。
TSLint規則目前只有true和false的選項,這致使告終果要麼正常,要麼報錯ERROR,而不會出現WARNING等警告。
有些時候,雖然配置某些規則開啓,可是某個文件內可能會關閉某些甚至所有規則檢查,這時候能夠經過規則註釋來配置,如:
/* tslint:disable */
複製代碼
上述註釋表示本文件自此註釋所在行開始,如下的全部區域關閉TSLint規則檢查。
/* tslint:enable */
複製代碼
上述註釋表示本文件自此註釋所在行開始,如下的全部區域開啓TSLint規則檢查。
/* tslint:disable:rule1 rule2 rule3... */
複製代碼
上述註釋表示本文件自此註釋所在行開始,如下的全部區域關閉規則rule1 rule2 rule3...的檢查。
/* tslint:enable:rule1 rule2 rule3... */
複製代碼
上述註釋表示本文件自此註釋所在行開始,如下的全部區域開啓規則rule1 rule2 rule3...的檢查。
// tslint:disable-next-line
複製代碼
上述註釋表示此註釋所在行的下一行關閉TSLint規則檢查。
someCode(); // tslint:disable-line
複製代碼
上述註釋表示此註釋所在行關閉TSLint規則檢查。
// tslint:disable-next-line:rule1 rule2 rule3...
複製代碼
上述註釋表示此註釋所在行的下一行關閉規則rule1 rule2 rule3...的檢查檢查。
以上配置信息,這裏具體參考了palantir.github.io/tslint/usag…。
在完成工程配置後,須要下載所須要依賴包,要在工程所在根目錄使用npm install
命令完成下載依賴包。
在完成下載依賴包後,IDE環境能夠根據對應配置文件進行提示,能夠實時地提示出存在問題代碼的錯誤信息,以VSCode爲例:
VSCode目前還有繼續完善的空間,若是部分文件未在窗口打開的狀況下,可能存在其中錯誤未提示出的狀況,這時候,咱們能夠經過本地命令進行全工程的檢查,在React Native工程的根目錄下,經過如下命令行執行:
tslint --project tsconfig.json --config tslint.json
複製代碼
(此命令若是不正確運行,可在以前加入./node_modules/.bin/)即爲:
./node_modules/.bin/tslint --project tsconfig.json --config tslint.json
複製代碼
從而會提示出相似如下錯誤的信息:
src/Components/test.ts[1, 7]: Class name must be in pascal case
複製代碼
本地進行代碼檢查的過程也會存在被人遺忘的可能性,經過技術的保障,能夠避免人爲遺忘,做爲代碼提交的標準流程,經過CI檢查後再合併代碼,能夠有效避免代碼錯誤的問題。CI系統能夠爲理解爲一個雲端的環境,環境配置與本地一致,在這種狀況下,能夠生成與本地一致的報告,在美團內部可使用基於Jenkins的Castle CI系統, 生成結果與本地結果一致:
代碼檢查不止侷限上述階段,在代碼commit、pull request、打包等階段都可觸發。
當前的TSLint規則雖然涵蓋了比較廣泛問題的一些代碼檢查,可是實踐中仍是存在一些問題的:
基於以上緣由其餘團隊也有自定義TSLint的先例,例如上文提到的tslint-microsoft-contrib、tslint-eslint-rules等。
那自定義TSLint大概須要什麼步驟呢,首先規則文件根據規範進行循序漸進的編寫規則信息,而後根據代碼檢查邏輯對語法樹進行分析並編寫邏輯代碼,這也是自定義規則的核心部分了,最後就是自定義規則的使用了。
自定義規則的示例直接參考官方的規則是最直接的,咱們能這裏參考一個比較簡單的規則"class-name"。
"class-name"規則上文已經提到,它的意思是對類命名進行規範,當團隊中類相關的命名不規範,會致使項目代碼風格不統一甚至其餘出現的問題,而"class-name"規則能夠有效解決這個問題。咱們能夠看下具體的源碼文件:github.com/palantir/ts…。
而後將分步對此自定義規則進行講解。
規則命名必須是符合如下2個規則:
規則的類名是Rule
,而且要繼承Lint.Rules.AbstractRule
這個類型,固然也可能有繼承TypedRule
這個類的時候,可是咱們經過閱讀源碼發現,其實它也是繼承自Lint.Rules.AbstractRule
這個類。
metadata包含了配置參數,定義了規則的信息以及配置規則的定義。
規則類型有四種,分別爲:"functionality"、"maintainability"、"style"、"typescript"。
這個主要是在檢查出問題的時候進行提示的文字,並不侷限於使用一個靜態變量的形式,可是大部分官方規則都是這麼編寫,這裏對此進行介紹,防止引發歧義。
apply
主要是進行靜態檢查的核心方法,經過返回applyWithFunction
方法或者返回applyWithWalker
來進行代碼檢查,其實applyWithFunction
方法與applyWithWalker
方法的主要區別在於applyWithWalker
能夠經過IWalker
實現一個自定義的IWaker
類,區別以下:
其中實現IWaker
的抽象類AbstractWalker
裏面也繼承了WalkContext
,
而這個WalkContext
就是上面提到的applyWithFunction
的內部實現類。
不管是applyWithFunction
方法仍是applyWithWalker
方法中的IWaker
實現都傳入了sourceFile
這個參數,這個至關於文件的根節點,而後經過ts.forEachChild
方法遍歷整個語法樹節點。
這裏有兩個查看AST語法樹的工具:
AST Explorer
優勢:
在AST Explorer能夠高亮顯示所選中代碼對應的AST語法樹信息。
缺點:
TypeScript AST Viewer
優勢:
每一個版本對應對kind信息數值可能會變更,可是對應的枚舉名字是固定的,以下圖:
從而這個工具能夠避免頻繁根據其數值查找對應信息。
缺點: 不能高亮顯示代碼對應的AST語法樹區域,定位效率較低。
綜上,經過同時使用上述兩個工具定位分析,能夠有效地提升分析效率。
經過ts.forEachChild
方法對於語法樹全部的節點進行遍歷,在遍歷的方法裏能夠實現本身的邏輯,其中節點的類爲ts.Node
:
其中kind爲當前節點的類型,固然Node
是全部節點的基類,它的實現還包括Statement
、Expression
、Declaration
等,回到開頭這個"class-name"規則,咱們的全部聲明類主要是class與interface關鍵字,分別對應ClassExpression
、ClassDeclaration
、InterfaceDeclaration
, 咱們能夠經過上步提到的AST語法樹工具,在語法樹中看到其爲一一對應的。
在規則代碼中主要經過isClassLikeDeclaration
、isInterfaceDeclaration
這兩個方法進行判斷的。
其中isClassLikeDeclaration
、isInterfaceDeclaration
對應的方法咱們能夠在node.js文件中找到:
判斷是對應的類型時,調用addFailureAtNode
方法把錯誤信息和節點傳入,固然還能夠調用addFailureAt
、addFailure
方法。
最終這個規則編寫結束了,有一點再次強調下,由於每一個版本所對應的類型代碼可能不相同,當判斷kind的時候,必定不要直接使用各個類型對應的數字。
完成規則代碼後,是ts後綴的文件,而ts規則文件實際仍是要用js文件,這時候咱們須要用命令將ts轉化爲js文件:
tsc ./src/*.ts --outDir dist
複製代碼
將ts規則生成到dist文件夾(這個文件夾命名用戶自定),而後在tslint.json文件中配置生成的規則文件便可。
以後在項目的根目錄裏面,使用如下命令既可進行檢查:
tslint --project tsconfig.json --config tslint.json
複製代碼
同時爲了將來新增規則以及規則配置的更好的操做性,建議能夠封裝到本身的規則包,以便與規則的管理與傳播。
在美團,有十餘個頁面的單個工程首次接入TSLint後,檢查出的問題有近百條。可是因爲開啓的規則不一樣,配置規則包的差別,檢查後的數量可能爲幾十條到幾千條甚至更多。如今已開發十餘條自定義規則,在單個工程內,處理優化了數百處可能存在問題的代碼。最終TSLint接入了相關React Native開發團隊,成爲了代碼提交階段的必要步驟。
經過團隊內部的驗證,文章開頭遇到的問題獲得了有效地緩解,目標基本達到預期。TSLint在React Native開發過程當中既保證了代碼風格的統一,又保證了React Native開發人員的開發質量,避免了許多低級錯誤,有效地節省了問題排查和人員溝通的成本。
同時利用自定義規則,可以將一些兼容性問題在內的個性化問題進行總結與預防,提升了開發效率,不用花費大量時間查找問題代碼,又避免了在一個問題上跌倒屢次的狀況出現。對於不一樣經驗的開發者而言,不只能夠進行友好的提示,也能夠幫助快速地定位問題,將一我的遇到的經驗教訓,用極低的成本擴散到其餘團隊之中,將開發狀態從「亡羊補牢」進化到「防患未然」。
家正,美團點評Android高級工程師。2017 年加入美團點評,負責美團大交通的業務開發。