RN程序员我爱编程

使用Flow来检测你的JS

2018-01-17  本文已影响3356人  lang_liu

最近在一篇文章上看见了关于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!

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

// @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官网中查看。

上一篇下一篇

猜你喜欢

热点阅读