使用Flow来检测你的JS

最近在一篇文章上看见了关于Flow的介绍,觉得它很不错,虽然之前在项目中使用Typescript已经很顺手了,再使用Flow感觉有点累赘了,但多学点总是没错的。

简介

JS作为一种脚本语言是没有类型检测的,这个特点有时候用着很爽,但当你在一个较大的项目中的时候,就会发现这其实是一件挺糟糕的事情,因为和你协作的程序员往往不太清楚你所写的代码到底哪种类型才是正确的,而且代码重构的时候也很麻烦。于是基于这个需求有了Typescript和Flow的产生,今天这里主要介绍Flow。

安装

因为笔者一直使用的是WebStorm,WebStorm内部对Flow就有一定的支持,所以如果你也使用WebStorm的话会方便很多。

yarn add --dev flow-bin babel-cli babel-preset-flow

在安装了上述的包之后,创建 .babelrc 文件:

{
  "presets": ["flow"]
}

设置WebStorm

通过 File>Settings>Languages&Frameworks>JavaScript 如下图所示设置,Flow package可以选择你项目下的flow-bin,当然你也可以全局安装flow-bin,然后在这里设置后就可以在每个项目中都使用Flow了 。

Flow-Settings

flow不能直接在node或浏览器环境中使用,所以我们必须用babel编译后才能使用:


Snipaste_2018-01-15_23-54-48.png

现在环境已经快配好了,只剩最后一步,将一个此项目初始化为一个Flow项目:

yarn run flow init
Snipaste_2018-01-16_00-00-14.png

现在当我们在项目中使用Flow时WebStorm可以给出智能的提示了。

使用

类型

最新的 ECMAScript 标准定义了 7 种数据类型:

在Flow中也是使用这几种类型作为标注:

使用原始类型:

// @flow
function method(x: number, y: string, z: boolean) {
  // ...
}

method(3.14, "hello", true);

使用对象类型:

// @flow
function method(x: Number, y: String, z: Boolean) {
  // ...
}

method(new Number(42), new String("world"), new Boolean(false));

这里需要注意的是大小写,小写的 number 是原始类型,而大写的 Number 是JavaScript的构造函数,是对象类型的。

Boolean

在Flow中,默认并不会转换类型,如果你需要转换类型请使用显示或隐式转换,例如:

// @flow
function acceptsBoolean(value: boolean) {
  // ...
}

acceptsBoolean(true);  // Works!
acceptsBoolean(false); // Works!
acceptsBoolean("foo"); // Error!
acceptsBoolean(Boolean("foo")); // Works!
acceptsBoolean(!!("foo")); // Works!

Number

// @flow
function acceptsNumber(value: number) {
  // ...
}

acceptsNumber(42);       // Works!
acceptsNumber(3.14);     // Works!
acceptsNumber(NaN);      // Works!
acceptsNumber(Infinity); // Works!
acceptsNumber("foo");    // Error!

null和void

JavaScript兼有 nullundefined。Flow将这些视为单独的类型:nullvoid(void表示undefined类型)

// @flow
function acceptsNull(value: null) {
  /* ... */
}

function acceptsUndefined(value: void) {
  /* ... */
}

acceptsNull(null);      // Works!
acceptsNull(undefined); // Error!
acceptsUndefined(null);      // Error!
acceptsUndefined(undefined); // Works!

也许类型

也许类型是用于可选值的地方,你可以通过在类型前添加一个问号(如 ?string 或者 ?number)来创建它们。

除了问号 ? 后跟着的类型,也许类型也可以是 null 或者 void 类型。

// @flow
function acceptsMaybeString(value: ?string) {
  // ...
}

acceptsMaybeString("bar");     // Works!
acceptsMaybeString(undefined); // Works!
acceptsMaybeString(null);      // Works!
acceptsMaybeString();          // Works!

可选的对象属性

对象类型可以具有可选属性,问号 ? 位于属性名称后面。

{ propertyName?: string }

除了它们的设定值类型之外,这些可选属性也可以被 void 完全省略。但是,他们不能 null

// @flow
function acceptsObject(value: { foo?: string }) {
  // ...
}

acceptsObject({ foo: "bar" }); // Works!
acceptsObject({ foo: undefined }); // Works!
acceptsObject({ foo: null }); // Error!
acceptsObject({}); // Works!

可选的函数参数

函数可以具有可选参数,其中问号 ? 出现在参数名称后面。同样,该参数不能为 null

// @flow
function acceptsOptionalString(value?: string) {
  // ...
}

acceptsOptionalString("bar");     // Works!
acceptsOptionalString(undefined); // Works!
acceptsOptionalString(null);      // Error!
acceptsOptionalString();          // Works!

文字类型

文字类型使用一个具体的值作为类型:

function foo(value: 2) {}

foo(2); // Work!
foo(3); // Error!
foo('2'); // Error!

您可以使用这些类型的原始值:

  • 布尔人: truefalse
  • 数字:像 423.14
  • 字符串:像 "foo""bar"
// @flow
function getColor(name: "success" | "warning" | "danger") {
  switch (name) {
    case "success" : return "green";
    case "warning" : return "yellow";
    case "danger"  : return "red";
  }
}

getColor("success"); // Works!
getColor("danger");  // Works!
// $ExpectError
getColor("error");   // Error!

混合类型 mixed

有时候我们并不能确定需要的值到底是哪种类型,这时候我们可以使用混合类型来表示,但在使用该值之前,我们需要判断该值到底是哪种类型,否则会引起错误:

// @flow
function stringify(value: mixed) {
  // $ExpectError
  return "" + value; // Error!
}

stringify("foo");
// @flow
function stringify(value: mixed) {
  if (typeof value === 'string') {
    return "" + value; // Works!
  } else {
    return "";
  }
}

stringify("foo");

任意类型 any

如果你想要一种方法来选择不使用类型检查器,any 是做到这一点的方法。

使用any是完全不安全的,应尽可能避免。

例如,下面的代码不会报告任何错误:

// @flow
function add(one: any, two: any): number {
  return one + two;
}

add(1, 2);     // Works.
add("1", "2"); // Works.
add({}, []);   // Works.

接口类型 interface

你可以使用 interface 以声明您期望的类的结构。

// @flow
interface Serializable {
  serialize(): string;
}

class Foo {
  serialize() { return '[Foo]'; }
}

class Bar {
  serialize() { return '[Bar]'; }
}

const foo: Serializable = new Foo(); // Works!
const bar: Serializable = new Bar(); // Works!

你也可以使用 implements 告诉Flow,你希望类匹配一个接口。这可以防止编辑类时发生不兼容的更改。

// @flow
interface Serializable {
  serialize(): string;
}

class Foo implements Serializable {
  serialize() { return '[Foo]'; } // Works!
}

class Bar implements Serializable {
  // $ExpectError
  serialize() { return 42; } // Error!
}

数组类型 Array

要创建一个数组类型,可以使用 Array<Type> 类型,其中 Type 是数组中元素的类型。例如,为你使用的数字数组创建一个类型 Array<number>

let arr: Array<number> = [1, 2, 3];

暂时就介绍这么多,还有一些类型文章中没有提到,更多更详细的内容请在Flow官网中查看。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,145评论 0 13
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,783评论 0 38
  • Yo Yannan: I don't dare to say ,one of my friends in my g...
    鸿鑫在简书阅读 557评论 0 0
  • 概念 JNI(Java本地方法):使得java程序越过JVM直接调用本地方法提供了一种便捷的方式; CAS:1.是...
    jadefly阅读 225评论 0 0