Flow:Facebook 的 JavaScript 靜態型別檢查器

邢敏發表於2014-11-27

11 月 18 日,Facebook 宣佈推出一個開源的 JavaScript 靜態型別檢查器,旨在發現 JS 程式中的型別錯誤,以提高程式設計師的效率和程式碼質量。本文譯自 Facebook 官博文章,FB 的三位工程師為大家簡要介紹了這個工具。


今天,我們很興奮要釋出 Flow 的先期版本,它是一個新的開源JavaScript靜態型別檢查器。Flow給JavaScript增加了靜態型別來提高開發人員的生產力和程式碼質量。特別是,靜態型別提供了一些極大的助益,如前期錯誤檢測,它可以幫助您避免某些種執行時故障;如程式碼的智慧提示,這有助於程式碼維護、導航、轉換和優化。

我們設計了Flow,使得開發人員能夠獲得它的裨益,同時不會失去在在JavaScript中編碼的“感覺”。Flow增加了最小的編譯時開銷,因為它會主動地在後臺做所有的工作。同時Flow並不強制你改變你如何編碼 ,它會執行復雜的程式分析來遷就你所熟悉和喜歡的習慣。

Flow仍處於早期階段,不過我們已經在Facebook上嘗試用它了。我們希望你會享受在你的專案使用它,並期待您的反饋。您可以訪問flowtype.org學習上手。

概述

Facebook是喜歡JavaScript的;它快速便捷,它富有表現力,它到處可以允許,這些使得它成為一個偉大的產品構建語言。同時,靜態型別的缺失拖慢了開發者。bug難於找到(如奔潰往往是離導致的根源很遠),程式碼維護噩夢一般(如在對全部的依賴項沒有完整認知的情況下,重構就是在冒險)。Flow提高了速度和效率,使開發人員可以在使用JavaScript更加高效率。

但分層靜態型別系統上的JavaScript很不簡單。 JavaScript積木般的語法非常富有表現力,而簡單的型別系統不足以精確建模它們的語義。為和幾種常見的JavaScript風格無縫協作,Flow採用的這種資料流和控制流分析,編譯器通常表現為從程式碼中提取語義資訊。然後使用這些資訊進行型別推導,構建型別理論的高階技術。當然了,設計一個功能強大的靜態分析還不夠 —JavaScript程式碼庫可能會很大,所以型別檢查必須速度極快以不打斷開發者的的編輯執行迴圈。Flow進行其模組化的分析,以模組的邊界指導型別。這樣可以進行激烈的並行增量式的型別檢查架構,很像Hack。這使得型別檢檢視上去只在轉瞬間,即使有數百萬行程式碼。

Flow的型別檢查是選擇在–你不需要立刻型別檢查所有你的程式碼。然而,Flow的設計基本是假設大部分的JavaScript程式碼被是隱式靜態型別;甚至型別可能根本未在程式碼的任何地方出現,他們在開發者的心中,用來推理程式碼的正確性。Flow儘可能地自動推斷出那些型別,這意味著它可以找到型別錯誤,而無需修改任何程式碼。在另一方面,一些JavaScript程式碼,尤其是框架,大量使用反射,往往是很難靜態推導的。對於這樣的固有動態程式碼,型別檢查會很不精確,所以Flow提供了一個簡單的方法來明確信任這樣的程式碼並繼續。這種設計在我們Facebook巨大的JavaScript程式碼庫中得到了驗證:我們的大多數程式碼是隱式的靜態型別,開發人員可以檢查他們程式碼的型別錯誤,而無需顯式標註的程式碼型別。

這使Flow跟現有的JavaScript型別系統(如 TypeScript)大為不同,現有的型別系統做了個較弱的假設,即大部分JavaScript程式碼是動態型別的,而且它是由開發者指明哪些程式碼可能適合做靜態型別化。在一般情況下,這種設計會導致以下範疇內的減益:較少型別的錯誤被發現,並且工具是不那麼有效。雖然這對於一些程式碼是個合理選擇,但一般而言這樣的設計不能提供足夠多的好處,因為它可能沒有明顯的額外工作。當然,必要時Flow提供在切換到型別檢查的這個弱模式,比如,檢查現有的程式碼時,這是個典型的有效簡單方法。

為了說明這個差別,看看這個小例子:

Flow將捕獲錯誤(我們正試圖乘一個數字和一個字串),而較保守的分析需要明確標註x的型別。在這個玩具例子裡,不會有問題,但對一個巨大的程式碼庫所需要標註的量可能過高。Flow可以發現程式裡的錯誤,而不需要註釋–當然,雖然程式設計師可以隨意新增型別註釋,如果他們選擇這麼幹。

 

型別系統

Flow的型別系統的許多特性都是可以預見的。對標準基本型別(數字,字串,布林)的支援,以及禁止它們之間的隱式轉換,除非有一小部分合法的情況下。結構型別,如那些用於函式,物件和陣列的,也支援。型別可以是多型的。

令人驚訝的是,Flow不會考慮null和undefined是上述型別的一部分。包括著他們的型別也是可能的型別,而這些型別的使用必須進行相應的檢查以防備之。對其他型別的聯合(例如string|number)也是支援的,而且其使用液需要進行類似的防備。Flow理解在收窄型別的動態檢查中的影響。

讓我們看一個例子來說明空值的處理。下面的程式崩潰總是在執行,但傳統的型別系統會認為這是很好的型別化:

Flow會在編譯時捕獲這個錯誤,並指出x可以為空(因此它的length屬性不應該被訪問)。此外,FLow瞭解到貫穿程式的控制流,所以這個程式的簡單變化會得到很好的型別化:

此外,Flow理解JavaScript的物件模型的複雜性:建構函式、方法、原型和其動態擴充套件和繫結。還有對於指定的物件複雜的操作,如合併物件,提取它們的鍵,等等型別的實驗支援。我們希望在未來這些特性將使使框架API可以指定精確的型別。

型別錯誤通常報告為值及其用途之間的不相容:例如,一個函式被呼叫時引數過少,物件中丟失的屬性被訪問,或一個字串作為一個數字使用。

最後,Flow支援動態型別(任何),它可以用於繞過期望的型別系統:例如,任何可以用來註釋的位置處的靜態分析是不精確的,並導致為型別錯誤(通常,使用了反射)。此外,切換Flow到上述型別檢查的弱模式會導致所有位置的未註明型別被假設為註解。

該型別系統的更多細節可以在文件中找到。

可伸縮性

要想可伸縮,Flow得分別檢查每個模組,利用其對模組與其它模組的依賴關係及其它模組的型別介面的所知。為生成這些型別的介面,Flow可能會要求型別標註在模組的邊界。

Flow在在後臺執行的持續服務維持著整個程式碼庫的語義資訊。當服務啟動時,Flow執行整個程式碼庫的初步分析。隨後,每當一組檔案被改變(由於個別檔案被儲存,或者被切換或重訂到一個開發分支),這個服務通過逐步檢查這些檔案和其他檔案的正確性,可能已經受到的變化,來更新其已知。這樣,當開發者請求型別錯誤時,它們是已經可用的服務,所以可以瞬時響應。這種伺服器架構是建立在和強大的Hack相同的技術上。

相容性

Flow致力於跟隨JavaScript的標準的發展。它已經支援各種ES6特性,如析構,類,擴充套件物件,可選函式引數,以及核心API的擴充套件(如Map,Set,Promise,以及Object,Array和Math的新函式)。其他特性(尤其是模組)也在開發中。Flow支援模組組織遵從CommonJS的/ Node.js的規範。

此外,Flow支援JSX和可用於型別檢查React 應用程式。比方說,你定義一個React 的類:

如果您在命名JSX字面量的React類時輸入了一個錯字,Flow將捕獲如下的錯誤:

此外,當您在React類中使用React.PropTypes標準時,你可以靜態地型別檢查JSX字面量的屬性,:

Flow現在會捕獲錯誤如屬性缺失,如<Hello/>,或者用了錯誤的型別屬性,如<Hello name={42}/>。

更多細節可以在Flow的React支援文件中找到。

開源

Flow實現了OCaml中的大部分。程式碼庫正在積極發展,並會繼續在未來數個月內迅速發展。除了工作在Facebook的程式碼庫,我們希望Flow的分析引擎的實現將是非常有用的,就像那些構建JavaScript和其他語言類似的開發工具一樣。如果你想合作,請讓我們知道!

要了解更多關於Flow,請下載嘗試一下,來看看flowtype.org,或者點選下面的按鈕。

Source Watch Fork

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式

Flow:Facebook 的 JavaScript 靜態型別檢查器 Flow:Facebook 的 JavaScript 靜態型別檢查器

相關文章