Flow.js学习笔记一

Flow.js

Flow.js是FaceBook发布的开源Javascript静态类型检查器。他给JavaScript提供了静态类型来提高开发人员的生产力和代码质量。

与Vue.js

这个选择最根本的还是在于工程上成本和收益的考量。Vue 2.0 本身在初期的快速迭代阶段是用 ES2015 写的,整个构建工具链也沿用了 Vue 1.x 的基于 ES 生态的一套(Babel, ESLint, Webpack, Rollup...),全部换 TS 成本过高,短期内并不现实。相比之下 Flow 对于已有的 ES2015 代码的迁入/迁出成本都非常低:
1. 可以一个文件一个文件地迁移,不需要一竿子全弄了。2. Babel 和 ESLint 都有对应的 Flow 插件以支持语法,可以完全沿用现有的构建配置;
3. 更贴近 ES 规范。除了 Flow 的类型声明之外,其他都是标准的 ES。万一哪天不想用 Flow 了,用 babel-plugin-transform-flow-strip-types 转一下,就得到符合规范的 ES。
4. 在需要的地方保留 ES 的灵活性,并且对于生成的代码尺寸有更好的控制力 (rollup / 自定义 babel 插件)第三点额外说一些:这一点上 Facebook 应该也是同样的考量,宁可贴近规范而不是用一个被 M$ 控制的、一旦使用难以迁出的语言。为什么 Angular 就愿意用 TS 呢?因为 Angular 本来就是一帮搞 Java 的人弄出来的,而且只是 Google 的一个子项目,Google 在公司层面根本不关心它用什么语言。而对 Facebook 来说 React/ReactNative/Babel/Flow/Nuclide 是整个公司 infrastructure 层面的东西,要么依赖规范,要么就得自己有控制权。编辑器 / IDE 方面,Atom + Nuclide 其实也还凑合,type warning / type hint / autocomplete / jump to definition 都有,就是 Atom 本身慢了点。至于重构、设计什么的,我只想说,看的是使用的人的水平,跟用什么语言没那么大关系。水平烂的人用 TS 一样写的是翔一样的代码,看看 java 就知道了。另外注意我并没有说 TS 不好,但是在 Vue 的需求和现状下 Flow 是更合理的选择。

这是Vue作者关于Flow和Ts的回答。

类型标注

Flow通过类型标注(static type annotations)来检查Javascript代码.这些标注让Flow知道你想在代码中使用何种类型,并且会保证运行的准确性。

    // @flow
    function square(n: number): number {
    return n * n;
    }
    square("2"); // Error!

实际上因为Flow对Javascript非常了解,并不需要对每个地方进行标注,你只需要做最少的必需的标注,而剩下的地方,Flow会自行推断。大部分时候,Flow不需要任何标注就可以理解你的代码。

    // @flow
    function square(n) {
    return n * n; // Error!
    }

    square("2");

基本类型

JavaScript有下面几种基本类型 (MDN):

  • Booleans
  • Strings
  • Numbers
  • null
  • undefined (在Flow中表示为void)
  • Symbols (ES2015中新出现的类型,Flow暂不支持)

Maybe类型

Maybe类型是当参数为可选时使用的类型,在参数前加?表示Maybe类型比如?string 或者 ?number。
Maybe类型同样可以接受null或者void作为合法的参数。

可选对象属性

对象类型可以有可选的属性,当属性前面加了?之后.

    { propertyName?: string }

同样作为可选属性,可以为null或者void.

可选函数参数

函数可以有可选的参数,只要在参数后加?

    function method(param?: string) { /* ... */ }

除了标注的类型,可选参数可以使用void或无参数,但不可以使用null.

默认函数参数

函数可以使用默认的参数

    function method(value: string = "default") { /* ... */ }

字面量类型

除了使用基本类型标注,Flow还可以使用字面量对参数标注。与联合类型结合使用可以实现强大的效果。

    // @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!

混合类型

混合类型,可以接受任何类型作为参数,但需要在函数内对参数进行处理。

    // @flow
    function stringify(value: mixed) {
    // ...
    }

    stringify("foo");
    stringify(3.14);
    stringify(null);
    stringify({});

Any类型

如果不想对代码进行类型检查,可以使用any标注,但是这样的代码是完全不安全的,尽量避免使用。
下面的代码将不会报错。

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

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

可变类型

当你声明一个变量时,可以同时声明他的类型,在JavaScript中有三种声明变量的方法:

  • var - 声明一个变量,可选赋值
  • let - 声明一个作用域内变量,可选赋值
  • const - 声明一个作用域内的变量,必须赋值,而且之后无法改变值。

在Flow中视为两种:

  • let 和 var - 可以被再次赋值的变量.
  • const - 不可被再次赋值的变量.

重点讲下let和var,当使用let和var声明变量时,如果使用了类型标注,则下面再次赋值时必须按照标注的类型赋值,否则将会报错。

    // @flow
    let foo: number = 1;
    foo = 2;   // Works!
    // $ExpectError
    foo = "3"; // Error!

当为变量重新赋值时,Flow会按照标注提供的所有可能的类型进行检查:

    let foo = 42;

    if (Math.random()) foo = true;
    if (Math.random()) foo = "hello";

    let isOneOf: number | boolean | string = foo; // Works!

Flow会判断变量的类型在重新赋值后发生了改变,在这种情况下,Flow会赋予安全的变量类型。

    // @flow
    let foo = 42;
    let isNumber: number = foo; // Works!

    foo = true;
    let isBoolean: boolean = foo; // Works!

    foo = "hello";
    let isString: string = foo; // Works!

函数类型

函数有两个地方接受类型标注:参数和返回值

    // @flow
    function concat(a: string, b: string): string {
    return a + b;
    }

    concat("foo", "bar"); // Works!
    // $ExpectError
    concat(true, false);  // Error!

剩余参数

JavaScript支持使用剩余参数,只需要在最后参数前面加上...,对剩余参数的标注只需要使用Array

    function method(...args: Array<number>) {
    // ...
    }

对象类型

对象在Javascript中被广泛使用,同样对他们的标注也有多种形式。

对象会尽可能符合标注的类型。使用花括号将标注的对象类型括起来,并使用,分割名称和值。

    // @flow
    var obj1: { foo: boolean } = { foo: true };
    var obj2: {
    foo: number,
    bar: boolean,
    baz: string,
    } = {
    foo: 1,
    bar: true,
    baz: 'three',
    };

可以为一个对象类型设置可选的属性标注

    // @flow
    var obj: { foo?: boolean } = {};

    obj.foo = true;    // Works!
    // $ExpectError
    obj.foo = 'hello'; // Error!

密封的对象

在Flow的语法中,当你创建了一个具有属性的对象,被视为创建了一个密封的对象,将不能向其中添加新的属性。

而如果你创建的是空对象,则可以继续添加新的属性。

准确对象类型

在Flow的语法中,向一个对象标注传递含有额外的属性的对象是被允许的

    // @flow
    function method(obj: { foo: string }) {
    // ...
    }

    method({
    foo: "test", // Works!
    bar: 42      // Works!
    });

但是有时候我们需要准确的描述对象类型标注,这时就需要准确类型标注的语法:

    {| foo: string, bar: number |}

作为maps的对象

最新版本的JavaScript标准中包含了Map类,但是使用对象作为map依然很常见,这种对象允许添加和取回新的属性,并且属性的类型并不确定,所以无法对其进行类型标注。

在这种情况下,Flow使用序号属性的语法为对象类型进行标注。序号属性允许使用符合序号属性类型标注的值对对象进行读写。

    // @flow
    var o: { [string]: number } = {};
    o["foo"] = 0;
    o["bar"] = 1;
    var foo: number = o["foo"];

序号属性可以被命名,当对象类型含有序号属性时,会假设对象含有指定序号的属性,但是遇到没有该属性的情况下,会在运行时报错。

    var obj: { [number]: string } = {};
    obj[42].length; // No type error, but will throw at runtime

序号属性可以与命名属性混合使用

// @flow
var obj: {
size: number,
[id: number]: string
} = {
size: 0
};

function add(id: number, name: string) {
obj[id] = name;
obj.size++;
}
posted @ 2017-07-20 12:00  alex_mistlion  阅读(594)  评论(0)    收藏  举报