Flow:Facebook 的 JavaScript 静态类型检查器

474 查看

【伯乐在线导读】: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