• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

使用Flow使您的JavaScript代码更健壮

大型团队开发的大型应用程序可以从原生 javascript 所缺乏的静态类型检查中受益。Flow 由 Facebook 开发来解决这个问题。它是一个静态类型检查器,可以集成到您的开发过程中,及早发现很多问题,并帮助您快速行动。

什么是流量?

Flow 是一个工具,可以检查您的带注释的 JavaScript 代码并检测各种问题,如果没有它,这些问题只会在运行时被发现(或者更糟的是,不会发现并破坏您的数据)。这是一个简单的例子。

// @flow
function getGreeting(name: string): string {
  return `Hi, ${name}`;
}const  http = require("http");
const  greeting = getGreeting("Gigi")
const port = 8888

console.log(`Listening on port ${port}...`)
http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write(greeting);
  response.end();
}).listen(port);

流与打字稿

在深入了解 Flow 的具体细节之前,值得将其与其他替代方案进行比较,尤其是 typescriptTypeScript 是微软开发的 javaScript 的严格超集。任何 JavaScript 程序也是 TypeScript 程序。 

TypeScript 添加了可选的类型注释,总体上与 Flow 具有相同的目的。但是,有一些重要的区别。TypeScript 是一种独立的编程语言,可以编译为 JavaScript,而必须删除 Flow 注释才能恢复为有效的 JavaScript。 

TypeScript 有很好的工具和 IDE 支持。Flow 正在迎头赶上(例如 JetBrains WebStorm 具有原生 Flow 集成)。

最重要的哲学差异是 Flow 强调稳健性。TypeScript 1.0 没有捕捉到空错误;TypeScript 2.0 在这方面达到了 Flow 的严格空值检查。但在其他方面,例如通用容器或类型,TypeScript 更宽容,允许各种类别的错误通过(仅检查结构类型, 检查名义类型)。

TypeScript 作为自己的语言添加了概念和语言特性,例如类、接口、可见性指示器(公共、私有、只读)和装饰器。这些功能使来自主流面向对象语言(如 C++、Java 和 C#)的人们更容易理解和使用。

安装

由于 Flow 注释不是标准的 JavaScript,因此需要在部署应用程序之前将其删除。以下是如何通过yarn安装 flow 和 flow-remove-types :yarn add --dev flow-bin flow-remove-types

您可以在 package.json 文件中添加几个脚本来自动化该过程:

  "scripts": {
    "build": "flow-remove-types src/ -d lib/",
    "prepublish": "yarn run build"
  }

在将代码发布到 npm 注册表之前,您应该运行 prepublish 脚本。

对于其他安装选项(例如使用 npm 或 babel),请查看 Flow 安装指南

要完成安装,请键入: yarn run flow init

这将创建所需的 .flowconfig 文件。

类型系统

Flow 有两个重要目标:精度和速度。它的类型系统旨在支持这些目标。

精确

精度是通过分析代码如何与类型交互来实现的,无论是注释的还是推断的。任何不匹配都会引发类型错误。带注释的类型支持名义类型,这意味着具有相同属性的两种不同类型相互区分,不能替代。变量的类型定义为变量可能接收的运行时值的集合。 

速度

由于模块化和分布式处理的结合,流程很快。文件被并行分析,结果通过高效的共享内存进行合并,完成全程序类型检查。

支持的类型

Flow 支持多种类型。除了原始类型,它还支持以下内容:

  • 目的

  • 大批

  • 任何

  • 也许

  • 多变的

  • 元组

  • 班级

  • 界面

  • 通用的

类型注释

Flow 允许您将类型以及剩余rict 变量和参数声明为选定的值:

type Two2Four = 2 | 3 | 4

function doubleIt(number: Two2Four) {
  return number * 2
}

console.log(doubleIt(3))

Output:

6

如果超出有效范围,您将收到错误消息:

console.log(doubleIt(3))

Output:

Error: src/main.js:30
 30: console.log(doubleIt(5)) // error
                          ^ number. This type is incompatible with the expected param type of
 24: function doubleIt(number: Two2Four) {
                               ^^^^^^^^ number enum


Found 1 error

您还可以定义复杂类型,包括子类型。在以下代码示例中,Warrior 类型是 Person 的子类型。fight()这意味着可以从函数中将 Warrior 作为 Person 返回。但是,禁止返回 null。

type Person = { name: string, age: number }
type Warrior = { name: string, age: number, strength: number }

let redWolf : Warrior = { name: "Red Wolf", age: 24, strength: 10 }
let skullCrusher : Warrior = { name: "Skull Crusher", age: 27, strength: 11 }

function fight(w1: Warrior, w2: Warrior): Person {
  if (w1.strength > w2.strength) {
    return w1
  }
  if (w2.strength > w1.strength) {
    return w2
  }

  return null
}

Output:


Found 1 error
$ flow
Error: src/main.js:47
 47:   return null
              ^^^^ null. This type is incompatible with the 
              expected return type of
 39: function fight(w1: Warrior, w2: Warrior): Person {
                                               ^^^^^^ object type


Found 1 error

为了解决这个问题,如果两个战士的力量相同,让我们返回年轻的战士:

function fight(w1: Warrior, w2: Warrior): Person {
  if (w1.strength > w2.strength) {
    return w1
  }
  if (w2.strength > w1.strength) {
    return w2
  }

  return (w1.age < w2.age ? w1 : w2)
}

let winner = fight(redWolf, skullCrusher)
console.log(winner.name)

Output:

Skull Crusher

Flow 允许通过类扩展、不变性、协变和逆变来进行更精确的控制。查看有关方差的 Flow 文档

配置

Flow 使用项目根目录中的 .flowconfig 配置文件。该文件包含几个部分,可让您配置 Flow 应检查的文件及其操作的许多方面。 

包括

[include]部分控制应检查哪些目录和文件。默认情况下始终包含根目录。部分中的路径[include]是相对的。单个星号是任何文件名、扩展名或目录名的通配符。两颗星是任何目录深度的通配符。这是一个示例[include]部分:

[include]
../externalFile.js
../externalDir/
../otherProject/*.js
../otherProject/**/coolStuff/

忽视

[ignore]部分是对 的补充[include]您在此处指定的文件和目录不会被流程检查。奇怪的是,它使用了不同的语法(OCaml 正则表达式)并且需要绝对路径。改变这一点在 Flow 团队的路线图上。

在此之前,请记住首先处理包含部分,然后处理忽略部分。如果包含并忽略相同的目录和/或文件,它将被忽略。为了解决绝对路径问题,通常在每一行前加上.*如果要忽略根目录下的目录或文件,可以使用<PROJECT_ROOT> 占位符代替.*这是一个示例[ignore]部分:

[ignore]
.*/__tests__/.*
.*/src/\(foo\|bar\)/.*
.*\.ignore\.js
<PROJECT_ROOT>/ignore_me.js

任何重要的 JavaScript 应用程序都使用大量第三方库。如果您提供包含有关这些库的类型信息的特殊 libdef 文件,Flow 可以检查您的应用程序如何使用这些库。 

Flow 会自动扫描项目的“flow-typed”子目录中的 libdef 文件,但您也可以在 [libs] 部分中提供 libdef 文件的路径。如果您维护一个由多个项目使用的 libdef 文件的中央存储库,这将非常有用。

如果目标库不提供自己的类型定义,则导入现有类型定义并创建自己的类型定义非常简单。看:

  • 流文档:库定义

  • 流程文档:创建库定义

  • GitHub:导入和使用库定义

胶带

Flow 有几个 lint 规则,您可以控制并确定如何处理它们。您可以从命令行、代码注释或[lints]配置文件部分配置规则。我将在下一节讨论 linting,但这里是如何使用该[lints]部分配置它:

[lints]
all=warn
untyped-type-import=errorsketchy-null-bool=off

选项

[options]部分是您可以告诉 Flow 如何在不值得拥有自己的部分的各种情况下表现的地方,因此它们都被组合在一起。

选项太多,无法在此处一一列出。一些更有趣的是:

  • all: 设置为 true 以检查所有文件,而不仅仅是带有 @flow 的文件

  • emoji:设置为 true 以将表情符号添加到状态消息

  • module.use_strict: 如果您使用添加了“use strict;”的转译器,则设置为 true;

  • suppress_comment:一个正则表达式,它定义了一个注释以抑制下一行中的任何流错误(对进行中的代码很有用)

查看配置选项的流程指南中的所有选项

版本

Flow 及其配置文件格式不断发展。[version]部分允许您指定配置文件的设计版本,以避免混淆错误。

如果 Flow 的版本与配置的版本不匹配,Flow 将显示错误消息。

以下是指定支持版本的几种方法:

[version]
0.22.0 

[version]
>=0.13.0 <0.14.0

[version]
^1.2.3

插入符号版本保持版本的第一个非零组件固定。因此^1.2.3扩展到范围 >=1.2.3 < 2.0.0,并^0.4.5扩展到范围 >= 0.4.5 < 0.5.0。

从命令行使用 Flow

Flow 是一个客户端-服务器程序。Flow 服务器必须正在运行,并且客户端连接到它(如果它没有运行,则启动它)。Flow CLI 有许多命令和选项,可用于维护和自省,以及临时覆盖 .flowconfig 中的配置。

键入flow --help显示所有命令和选项。要获得有关特定命令的帮助,请键入flow <command> --help例如:

$ flow ast --help
Usage: flow ast [OPTION]... [FILE]

e.g. flow ast foo.js
or   flow ast < foo.js

  --from    Specify client (for use by editor plugins)
  --help    This list of options
  --pretty  Pretty-print JSON output
  --tokens  Include a list of syntax tokens in the output
  --type    Type of input file (js or json)

重要的命令是:

  • init:生成一个空的 .flowconfig 文件

  • check:做一个完整的流程检查并打印结果 

  • ls: 显示对 Flow 可见的文件

  • status(默认):显示来自 Flow 服务器的当前 Flow 错误

  • suggest: 建议目标文件的类型

流线型

Flow 有一个 linting 框架,可以通过前面看到的 .flowconfig 文件、命令行参数或使用 flowlint 注释在代码文件中进行配置。所有配置方法都包含一个键值对列表,其中键是规则,值是严重性。 

规则

目前有三个规则:all、untyped-type-import 和 sketchy-null。“全部”规则实际上是对没有更具体规则的任何错误的默认处理。当您从无类型文件导入类型时,会调用“无类型导入”规则。当您对可能为假或空/未定义的值进行存在检查时,将调用“粗略空”规则。有更细化的规则:

  • 粗略的空布尔

  • 粗略的空数

  • 粗略的空字符串

  • 粗略零混合

严重程度

还有三个严重级别:关闭、警告和错误。可以想象,“off”会跳过类型检查,“warn”会产生警告,这不会导致类型检查退出,并且默认情况下不会出现在 CLI 输出中(您可以使用 看到它们--include-warnings),并且“错误”的处理方式与流程错误一样,会导致类型检查退出并显示错误消息。

使用命令行参数检查

使用--lints 命令行参数指定多个 lint 规则。例如:

flow --lints "all=warn, untyped-type-import=error, sketchy-null-bool=off"

使用 flowlint 进行 Linting

共有三种类型的注释:flowlint、flowlint-line 和 flowlint-next-line。

“flowlint”注释在块中应用一组规则,直到被匹配的注释覆盖:

import type {
  // flowlint untyped-type-import:off
  Foo,
  Bar,
  Baz,
  // flowlint untyped-type-import:error
} from './untyped.js';

如果没有匹配的注释,则设置将应用到文件末尾。

“flowlint-line”仅适用于当前行:  

function (x: ?boolean) {
  if (x) { // flowlint-line sketchy-null-bool:off
    ...
  } else {
    ...
  }
}

“flowlint-next-line”适用于注释后的行:

function (x: ?boolean) {
  // flowlint-next-line sketchy-null-bool:off
  if (x) {
    ...
  } else {
    ...
  }
}

结论

大型团队开发的大型 JavaScript 项目可以从静态类型检查中受益匪浅。有几种解决方案可以将静态类型检查引入 JavaScript 代码库。 

文章目录
  • 什么是流量?
  • 流与打字稿
  • 安装
  • 类型系统
    • 精确
    • 速度
    • 支持的类型
  • 类型注释
  • 配置
    • 包括
    • 忽视
    • 胶带
    • 选项
    • 版本
  • 从命令行使用 Flow
  • 流线型
    • 规则
    • 严重程度
    • 使用命令行参数检查
    • 使用 flowlint 进行 Linting
  • 结论