作者:滴滴公共前端團隊 - J
前言:
很多人都開始一上來就應用到了 Vue.js 2.*,但並不是所有人都會發覺 Flow 的存在,其實熟悉 react 原始碼的應該不會陌生。所以呢,今天我們來普及一下這個東西到底對我們的工程成本有什麼收益呢?
它和 Babel 還有我們內部常用的 ESlint 有什麼外掛支援?
它和 Crockford 的 JSLint 或者後面的 JSHint 有什麼區別?
正文:
facebook 推出的 js 靜態型別檢查工具。
flow可以在程式碼執行前對型別錯誤進行檢查,包括:
- 型別錯誤
- 對null的引用
- 以及可怕的 “undefined is not a function”
flow 允許我們給變數新增型別
flow 的安裝和使用
安裝:
由於 flow 是用 OCaml 語言寫的,npm 上只有對應的二進位制包。
npm install --save-dev flow-bin複製程式碼
1.基本使用
安裝完成後我們在要執行靜態檢查的檔案跟目錄下執行一下 flow init ,之後會發現多處一個.flowconfig檔案,這個檔案告訴 Flow 在這個目錄下開始檢測。此外 .flowconfig 檔案可以進行一下更為高階的配置,比如僅包含一些目錄、忽略一下目錄等等(更深入的瞭解,請戳官網)。
對於需要使用 flow 進行型別檢查的 js 檔案,在開頭加入 @flow 的註釋
/* @flow */ 只要帶有這個註釋,都會進行型別檢測
或者
/* @flow weak */ 只對有加型別註解的變數進行型別檢測複製程式碼
例如:
/* @flow */
function multiple10 (num) {
return num * 10
}
multiple10('20')
function getLength (str) {
return str.length
}
getLength('3')
getLength([1,2,3])複製程式碼
接下來執行 flow check 看一下結果
3: return num * 10
^^^ string. The operand of an arithmetic operation must be a number.
Found 1 error複製程式碼
multiple10 函式中的型別轉換被 flow 標記出。
2.型別註解
注意上面例子中 flow 的報錯,只有 multiple10 中靜態型別錯誤被檢測中。對於 getLength 函式中引數 str 的型別是什麼呢? 從函式本身來分析,只要包含 length 屬性就都是合法的。 對於這種情況則可以為其新增“型別註解”,來明確的告訴 flow 這個值的型別。
function getLength (str: string) {
return str.length
}
getLength('3')
getLength([1,2,3])複製程式碼
再來執行一下 flow check , 這時結果會提示我們[1,2,3]這個引數型別不對。
12: getLength([1,2,3])
^^^^^^^ array literal. This type is incompatible with the expected param type of
8: function getLength (str: string) {
^^^^^^ string複製程式碼
3.自定義型別
很多時候,除了 number 、 string 這些基礎型別外,我們還會有一些自定義的型別,比如:
var someData = {
id: 1,
text: '選項1'
}複製程式碼
這時候可以在一個單獨的檔案中將 someData 申明瞭一個自定義型別。方式如下:
/* /decls/data.js.flow */
declare type SomeData = {
id: number;
text: strin;
}複製程式碼
然後在 .flowconfig 檔案中引入該申明檔案
[libs]
decls/複製程式碼
flow server
在大型專案中,如果每修改完程式碼,就執行以下 flow check ,然後等待看結果,顯然會被逼瘋的。flow 為我們提供了一個 flow server ,支援在後臺執行,並且只監測有修改的檔案。方法很簡單,只有一個命令
$> flow # 開啟一個後臺服務,輸出首次檢測結果
$> flow # 第二次使用flow,連線正在執行的後臺服務,輸出檢測結果
$> flow stop # 關閉flow server複製程式碼
babel+flow
由於 flow 中型別註解的語法不屬於 javascript 規範中的內容。所以在最終的程式碼中,我們需要移除flow的內容。flow 提供了 flow-remove-types 和 babel 外掛兩種方式,推薦使用 babel 外掛來完成這項工作。
flow-remove-types
這種方法比較簡單粗暴: 安裝 flow-remove-types,然後執行命令。$> npm install -g flow-remove-types $> flow-remove-types src/ --out-dir build/複製程式碼
babel外掛
安裝 babel 外掛$> npm install babel-plugin-transform-flow-strip-types複製程式碼
babel 的 plugin 中加入該外掛
{ "presets": ["es2015", "stage-2"], "plugins": ["transform-runtime", "transform-flow-strip-types"], "comments": false }複製程式碼
注意:在 babel6 的 babel-preset-react 的外掛中已經內建了 transform-flow-strip-types(flowtype.org/docs/syntax…),如果使用了 babel-preset-react 那麼無需再引入transform-flow-strip-types
eslint
eslint-plugin-flowtype 外掛,可以讓我們在 eslint 程式碼檢查中加入 flow 的書寫規範化檢查。使用方式也很簡單:
安裝
$> npm install eslint-plugin-flowtype複製程式碼
- 在 .eslintrc.js 中設定 parser 為 babel-eslint
- plugin 中加入 flowtype
- eslint-plugin-flowtype 外掛中預設提供了一份基於優秀實踐總結出的 flow type 書寫規範配置,在 .eslintrc 檔案的 extend 中加入 "plugin:flowtype/recommended" 即可直接使用。
{
"extends": [
"standard",
"plugin:flowtype/recommended"
],
"plugins": [
"flowtype"
]
}複製程式碼
完成後我們來看看效果。下面的程式碼
function getLength (str:string) {
return str.length
}
getLength('3')複製程式碼
eslint 檢查結果會丟擲一個錯誤:型別註解的冒號後面丟失了空格。
✘ https://google.com/#q=flowtype%2Fspace-after-type-colon There must be a space after "str" parameter type annotation colon
/Users/didi/xiaoju/src/static-h5-src/750/driver/feedback/src/main.js:2:21
function getLength (str:string) {
^
✘ 1 problem (1 error, 0 warnings)複製程式碼
因為推薦的規範中:型別註解冒號後需要一個空格
"rules": {
...
"flowtype/space-after-type-colon": [
2,
"always"
],
...
}複製程式碼
flow type 規範配置如:函式返回型別是否必須、型別註解冒號前後的空格、自定義的type 的名稱的命名方式等等,官方給出了很詳細說明和例子。注:針對flow的的規範規則配置前新增“flowtype/”
配置名稱 | 作用 | |
---|---|---|
boolean-style | 型別註解中布林值使用boolean還是bool | |
define-flow-type | 將型別註解標記為已定義,no-undef的檢查中不會出現報錯 | |
delimiter-dangle | Object和Tuple型別定義中分隔符使用規範 | |
generic-spacing | 泛型物件的尖括號中型別前後的空格規範 | |
space-before-generic-bracket | 泛型物件的尖括號前的空格規範 | |
no-dupe-keys | object型別的定義中是否有重複的屬性值 | |
no-primitive-constructor-types | 禁止使用原生的型別 | |
no-weak-types | 是否可以使用弱型別any、Object、Function | |
object-type-delimiter | Object型別定義中,屬性之前分割符為分號/逗號(注:該屬性已被廢棄,需要使用分號來分割) | |
require-parameter-type | 函式的引數是否需要型別註解 | |
require-return-type | 函式返回值是否需要型別註解 | |
require-valid-file-annotation | 檔案開頭@flow的寫法 | |
require-variable-type | 什麼樣的變數是需要型別註解 | |
semi | 使用type自定義型別語句結尾是否需要分號結尾 | |
sort-keys | Object型別定義中屬性排列順序 | |
space-after-type-colon | 型別註解分號後的空格規範 | |
space-before-type-colon | 型別註解分號前的空格規範 | |
type-id-match | 使用type自定義型別的名稱規範 | |
union-intersection-spacing | union型別、intersection型別連線符號\ | 、&之間的空格規範 |
use-flow-type | 將通過declare定義的自定義型別標記為已經被使用過,no-unused-vars的檢查中不會出現報錯 |
名詞說明
文中一些中文詞直接從英文文件中翻譯過來,可能有不準確的地方,這裡給出原文,避免歧義。
- 型別註解:原文為 type annotations ,標記變數的型別
自定義型別:原文為 type aliases, 類似C語言中的typedef,可以為已有型別定義一個新的名稱,或將一個複雜型別進行封裝,如
type a = Array<String> type Person = { name: string, age: number };複製程式碼
相關連結
flow 官網(關於 flow的各種用法官網給出了詳細的例子):flowtype.org/
flow 簡短教程:www.youtube.com/watch?v=xWM…
「掘金技術徵文」活動:gold.xitu.io/post/58522d…
歡迎關注DDFE
GITHUB:github.com/DDFE
微信公眾號:微信搜尋公眾號“DDFE”或掃描下面的二維碼