今天我們興奮的發布了 Flow 的嚐鮮版,一個新的Javascript靜態類型檢查器。 Flow為Javascript添加了靜態類型檢查,以提高開發效率和程式碼品質。更明確的說,靜態類型檢查提供的好處像早期錯誤檢查,幫助你發現一些只有在運行時才能發現的錯誤,以及代碼智能感知,它會幫助代碼維護,查找,重構和優化。
我們設計Flow的所有功能都建構在現有Javascript規格之上。因為Flow主動地在後台工作,所以額外的編譯開銷很小。 Flow並不要求開發者如何寫程式碼 —— 她用一套複雜的演算法分析你熟悉的程式碼風格。
Flow仍然在初期階段,但是我們已經在Facebook使用了。我們希望你在自己的專案中愉快的使用,期待你的回饋。可以存取 flowtype.org 快速開始。
總覽
Facebook超愛Javascript;它快,表達性好,而且到處運行,是建立產品的絕佳語言。同時,因為沒有靜態類型讓開發者困擾。 Bug難以發現(例如,崩潰的原因隱藏很深),程式碼維護猶如噩夢(例如,在不知道所有依賴的情況下進行重構風險很大)。 Flow改進了速度和效率促進了開發者在使用Javascript的生成效率。
在Javascript之上增加一層靜態系統並不簡單。 Javascript的積木(building block)表現力極高,一個簡單的型別系統並不能精確地組合出應有的語意。為了支援不同的Javascript程式設計範式和習慣,Flow引入了類似資料流(data-flow)和控制流(control-flow)這類通常用於編譯時提取語義的分析技術。然後用提取的信息,加上先進的類型原理來做類型推斷。當然,只有一個強力的靜態類型分析還不夠 —— Javascript程式碼庫會很大,這要求類型檢查必須閃電般快速,才能不打斷開發者編輯-運行的流程。 Flow按模組執行分析,所有的類型都限制在模組邊界以內。這最終形成一個高度平行、增量式的檢查架構,類似 Hack 。這使得類型檢查響應快速,即使是百萬行級別程式碼。
Flow的類型檢查是選擇性的 —— 你不需要一次執行檢查所有。然而,Flow背後的設計是基於假定大多數Javascript的程式碼類型是隱式靜態類型;雖然類型可能不會到處在程式碼中出現,它們是以一種可以按照程式碼正確性推理出來的形式存在於開發者的思路中。一旦可能,Flow就去推斷這些類型,意味著它可以不需要改動程式碼就能發現類型錯誤。另一發面,有些如存在於框架中的Javascript程式碼,大量使用了反射使得靜態類型推斷非常困難。對於這種天然動態的程式碼,類型檢查就會錯漏百出,因此Flow提供對此類程式碼添加信任並繼續。這種設計在Facebook內部被大量的Javascript程式庫所驗證:大多數程式碼沒有透過隱式靜態類型檢查條目,這些條目讓開發者可以不用添加註解就能檢查程式碼類型錯誤。
這使得Flow從根本上區別於其他Javascript的類型系統(如TypeScript),透過弱化的假設大多數JavaScript程式碼是動態輸入的,並由開發者自己表達哪些程式碼應該是靜態型別。通常來看,這類設計會導致檢查覆蓋率降低:更少的類型錯誤被偵測到,工具不夠有效率。然而對於某些情況下是合理的,一般這種設計如果沒有通過大量額外的努力就無法對實際開發提供足夠多的幫助。儘管如此,Flow讓你可以簡單就獲得這種弱化的類型檢查,對於現有程式碼非常有用。
為了解釋這種區別,請看下面的例子:
function onlyWorksOnNumbers(x) { return x * 10; } onlyWorksOnNumbers(‘Hello, world!');
Flow能够发现这个错误(尝试把数字和字符串相乘),然而另一种更加保守的分析需要显式的标注 x 的类型。在这个玩具般的例子里面并不觉得费力,但是在巨型代码库里面几乎无人去做。Flow可以不用添加注释就能发现这个错误 —— 当然前提是开发者想这样做。
类型系统
Flow的类型系统实现了许多期望中的功能。支持标准基本类型( number , string , boolean ),类型之间的隐式转换在除一些特殊情形外是被禁止的。结构类型,如函数、对象和数组也被支持。类型可以是多态的。
也许你会感到意外,Flow没有把 null 和 undefined 当成是上述类型中的任何一种。这两种类型会有多种可能,使用这些类型必须在合理检查的保护之上。其它组合类型(如 string | number )也被支持,这种用法同样需要确保安全。Flow知道缩小类型范围时做动态检查的影响。
让我们用一个例子来描述处理 null 值。下面的程序总是在运行时崩溃,但是一般的类型系统会认为它没有问题:
function length(x) { return x.length; } var total = length('Hello') + length(null);
Flow会在编译时期发现这个错误,并指出 x 可以是null( length 属性不应该被访问)。另外,Flow了解这个程序的控制流,所以简单修改就能让这个程序类型正确:
function length(x) { if (x !== null) { return x.length; } else { return 0; } } var total = length('Hello') + length(null);
Flow还了解JavaScript复杂的对象模型:构造器,方法,原型和它们动态扩展以及绑定。已经试验性去支持类型的复杂操作如:绑定对象,抽取keys等等。我们希望未来这些功能使得让为框架指定具体类型成为可能。
类型错误通常报告为定义和实际值不兼容:比如函数调用的参数不足,对象中不包含要访问的属性,或者把字符串当成数字使用。
最后,Flow支持动态类型( any ),这种类型可以绕过类型系统检查:比如可用 any 表示静态分析无法准确判断而报错的location(通常使用反射的情况)。另外Flow在弱模式下遇到上述类型且没有注释类型的话,会自动假定为 any 。
扩展性
为了拓展,Flow根据模块和其它模块的依赖关系以及其它模块提供的类型接口,单独对每个模块进行检查。要生成类型接口,Flow可能需要在模块边界上进行注释。
Flow在一个后台运行的持久化服务器上,维护着整个代码库的语义信息,一开始Flow会对整个代码做一次分析,然后当一系列文件改动的时候(可能是单个文件改动或者在切换分支的时候),服务器会增量式更新改动文件以及由于类型关联的其它相关文件的语义信息。这样,当开发者试图获取类型错误时,它们已经在服务器上了,相应几乎是立即的。这种服务器架构与 Hack 构建在同一种技术之上。
兼容性
Flow致力于支持最新的JavaScript标准。目前已经支持各种ES6特性如destructuring, classes, extended objects, optional function parameters,以及核心API扩展(比如Map, Set, Promise, 和 new methods on Object, Array, 和 Math)。其它特性(尤其是模块)正在开发中。Flow支持CommonJS / Node.js 规范的模块。
var Hello = React.createClass ({ render: function() { return <div>Hello {this.props.name}</div>; } });
如果你在JSX上使用的class名字有错误,Flow会发现这个问题:
React.render(, ...);
而且,如果你在React class里面使用了React.PropTypes规范,你可以对JSX上的attributes做静态类型检查:
var Hello = React.createClass ({ propTypes: { name: React.PropTypes.string.isRequired } ... });
Flow就会发现
更多的关于支持React的细节可以在 文档 中找到。
开源
Flow代码大部分用OCaml实现。代码库在活跃更新并且会在未来几个月快速进化。除了在Facebook范围内的数据代码库中运行外,我们希望Flow的分析引擎能用于构建类似的,无论是JavaScript或者其他的语言工具。请让我们知道你是否想加入!
好了,关于Flow之一个新的Javascript静态类型检查器的全部内容先给大家介绍到这里,后续还会持续更新,敬请关注!