FlowType簡易入門指北
- 2019 年 12 月 5 日
- 筆記
FlowType簡易入門指北
寫了一段時間JavaScript了,作為一個弱類型語言,無視類型判斷在開發過程中帶來了很多的好處,
int與float的轉換、string與int的拼接。都可以直接通過一元運算符得到結果。 但同樣的,程式碼量上去了以後,整個項目會變得非常複雜。 在開發過程中很難看出一段程式碼執行後的結果,或者一個函數參數/返回值的結構。 有很多bug是在運行時才發現的。比如一些常見的坑: $input.value + 1 // 如果input的值為 `2` 我們得到的結果卻是 `21` 以及在多人合作開發時,我們可能會提出一些公共的函數供其他人調用,例如: function numberAddComma (num) { return num // 添加千分位 } 如果不寫注釋的話,用的人很難去知道,這個參數到底是一個
number類型還是一個string類型。
於是,我們就有了大佬們創造的FlowType,一個靜態類型檢查工具。 Flow可以在程式碼運行前對類型進行檢查,包括:
- 類型錯誤
- 對null的引用
- 最坑爹的
undefined is not a function
Flow的安裝
我們可以通過直接安裝flow的npm包來應用。
npm i -D flow-bin
然後在package.json中添加一行命令:
{ "scripts": { "flow": "flow" } }然後我們在項目根目錄創建一個配置文件.flowconfig。 flow-bin提供一個命令生成一個空文件:
npm run flow init

在寫完程式碼後通過執行npm run flow即可進行校驗。
P.S. Flow只會檢查帶有@flow注釋標記的文件。
Flow的使用
Flow具有兩種類型檢查方式:
- 根據變數在程式碼中的運用來推斷變數的類型
- 通過事先聲明好的類型來進行檢查
通過程式碼來推斷類型
// @flow function maxNum (nums) { return Math.max.apply(null, nums) } maxNum(true) function joinStr (arr) { return arr.join(' ') } joinStr(123)以上程式碼執行npm run flow後,我們就會在終端看到如下報錯資訊

第一處表示apply預期第二個參數需要是支援迭代的,而我們傳入的一個boolean類型變數是不支援的。 第二處則是提示我們number類型是沒有實現join這個方法的。
// @flow function product(num1, num2) { return num1 * num2 } product(1, 2)以上程式碼的執行是不會出錯的,因為兩個number相加,這個是沒有問題的。
以上的所有檢測都是由Flow判斷程式碼得來的,對我們現有程式碼的改動是非常小的,基本上就是在文件頭部添加一個@flow標識就能完成Flow的配置。
固定類型
當然,完全依賴Flow去判斷類型,對於一些基礎的類型還是很好用的。 但如果遇到一些比較複雜的情況,還是建議直接在程式碼中添加類型的描述。
一個小例子:

就像上邊的函數,+這個運算符既可以用在string也可以用在number上,所以在Flow自行判斷來看,這個參數的類型就是number | string,而我們想要限制他為一個number 就只能自己添加類型的描述了。 然後我們將result的類型改為string觀察一下:

這時我們就能看到拋出的異常,提示result的類型與函數numberAdd的返回值類型不匹配。 同理,如果我們在調用函數時傳入一個string,Flow也會提示我們,類型不匹配,這極大的避免了因為類型轉換帶來的bug。
這個是最基本的靜態類型檢查效果,或者我們可以提前定義一些特殊的數據格式。

然後我們在.flowconfig文件中添加它的引用。(如果沒有這個文件請在根目錄創建,用來進行一些Flow相關的參數配置,官網文檔有詳細的解說)


然後我們在一個文件中進行應用,創建一個函數,用來輸出我們的Person對象的兩個屬性。
/* @flow */ function logPerson(person: Person): void { console.log(` firstName: ${person.firstName} age: ${person.age} `) } let person: Person = { // 後邊的 : Person 是可選的,因為會應用到我們前邊所說的`推斷類型` firstName: 'Bellic', age: 18 } logPerson(person)如果我們將變數person的age改為string類型,我們會看到如下的錯誤提示:

當然,關於類型,並不是只限定的只有一種類型,你可以在後邊添加多個類型,例如:
// @flow function getResult (num: string | number): number { console.log(num) return 1234 } let result1 = getResult(1) let result2 = getResult('2') let result3 = getResult(true) // error對空值的處理
// @flow const fs = require('fs') function readFile(filePath) { let buffer = fs.readFileSync(filePath) } readFile()如果我們寫了這樣的一個函數,用來獲取文件對應的數據的,在下邊調用時沒有傳入filePath參數,Flow會給出我們提示:

強制要求我們對空值進行處理。
function readFile(filePath) { if (!filePath) return '' let buffer = fs.readFileSync(filePath) }這樣Flow就不會報錯了,避免了程式碼上線後出現與undefined/null相關的可怕問題。
在IDE中的使用
每次寫完程式碼,都要去執行一下
npm run flow,其實也挺煩人的,所以,我們可以直接將Flow應用到編輯器上。 關於Flow的應用,因為Atom編輯器支援的還不太好,所以搞了VSCode來測試。 我所使用的是ESLint版本的
首先我們需要安裝Flow對應的eslint插件。
npm install --save-dev eslint eslint-plugin-flowtype
安裝完插件後,還需要去VSCode中安裝對應的Flow以及ESLint插件。


這些都完成後可能還會發現一些錯誤提示,類似:':' can only be used in a .ts file 這個是VSCode默認的一些錯誤檢查,解決這個只需要在IDE的配置文件中添加如下設置即可"javascript.validate.enable": false(command + ,)
這是我們就可以在IDE中直接看到Flow的類型檢查了。


使用VSCode還有一個好處,當你寫了一個應用了Flow的函數後,在調用函數時,游標懸浮在函數上,可以很直觀的看到函數的簽名:

以及如果我們將前邊定義的Person結構拿過來,在簽名中也是會直接體現出來的

移除Flow內容
因為Flow的語法並不是標準的JavaScript語法,所以我們要在程式碼最終上線前移除Flow相關的程式碼(主要是那些固定類型的描述,如果只是添加了
@flow,直接應用即可)
flow-remove-types
這個程式會將你所有標有@flow的內容進行移除。。然後將移除後的程式碼生成後指定的目錄下
npm i -g flow-remove-types flow-remove-types src/ --out-dir dist/ # src 源文件地址 # dist 生成後的地址
babel+webpack
安裝一個webpack插件
npm i -D flow-babel-webpack-plugin
然後我們修改 .babelrc文件,添加如下配置:
{ "plugins": [ "transform-flow-comments" ] }然後在webpack.config.js文件中添加:
module.exports = { plugins: [ new FlowBabelWebpackPlugin() ] }在babel編譯JavaScript的同時也就會將Flow內容進行移除了。
參考鏈接
關於Flow,個人感覺是一個挺好玩的東西,而且最重要的是,這個檢查器的遷移成本非常低,低到甚至只是添加一個/* @flow */就可以使用Flow相關的功能了。 對現有程式碼的破壞幾乎可以忽略不計。 只做一件事,且命中痛點,避免因變數類型造成的程式bug

