vue源码系列2--Flow.js

认识 Flow

JavaScript 是动态类型语言,它的灵活性有目共睹,但是过于灵活的副作用是很容易就写出非常隐蔽的隐患代码,在编译期甚至看上去都不会报错,但在运行阶段就可能出现各种奇怪的 bug。

flow.js是 facebook 出品的 JavaScript 静态类型检查工具,flow.js的理念类似于typescript,但是他比typescript更轻,更容易迁移,因为我们只需要做出很小的改变就可以使用flow.js,而typescript却非如此,它就像一门新的语言。

所以Vue.js 在做 2.0 重构的时候,在 ES2015 的基础上,除了 ESLint 保证代码风格之外,也引入了 Flow 做静态类型检查。

Flow 的工作方式

通常类型检查分成 2 种方式:

  • 类型推断:通过变量的使用上下文来推断出变量类型,然后根据这些推断来检查类型。

  • 类型注释:事先注释好我们期待的类型,Flow 会基于这些注释来判断。

类型推断
    
    function split(str) {
      return str.split(' ')
    }
    
    split(11)
    
    //这时候会报错,因为期待的是string类型
复制代码
类型注释

类型注释和TypeScript大同小异,举个例子

    function add(x: number, y: number): number {
      return x + y
    }
    
    add('Hello', 11)
    
    //此时报错,因为我们期待的是number
    后面的:number 是指返回的也是number类型
复制代码
对象类型:
    function method(x: Number, y: String, z: Boolean) {
      // ...
    }
    
    method(new Number(42), new String("world"), new Boolean(false));
复制代码
Boolean

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

    function acceptsBoolean(value: boolean) {
      // ...
    }
    
    acceptsBoolean(true);  // Works!
    acceptsBoolean(false); // Works!
    acceptsBoolean("foo"); // Error!
    acceptsBoolean(Boolean("foo")); // Works!
    acceptsBoolean(!!("foo")); // Works!
复制代码
Number
    function acceptsNumber(value: number) {
      // ...
    }
    
    acceptsNumber(42);       // Works!
    acceptsNumber(3.14);     // Works!
    acceptsNumber(NaN);      // Works!
    acceptsNumber(Infinity); // Works!
    acceptsNumber("foo");    // Error!
复制代码
null和void

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

    function acceptsNull(value: null) {
      /* ... */
    }
    
    function acceptsUndefined(value: void) {
      /* ... */
    }
    
    acceptsNull(null);      // Works!
    acceptsNull(undefined); // Error!
    acceptsUndefined(null);      // Error!
    acceptsUndefined(undefined); // Works!
复制代码
也许类型

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

    function acceptsMaybeString(value: ?string) {
        // ...
    }
    
    acceptsMaybeString("bar");     // Works!
    acceptsMaybeString(undefined); // Works!
    acceptsMaybeString(null);      // Works!
    acceptsMaybeString();          // Works!
复制代码
可选的对象属性

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

    { propertyName?: string }
复制代码

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

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

    acceptsObject({ foo: "bar" }); // Works!
    acceptsObject({ foo: undefined }); // Works!
    acceptsObject({ foo: null }); // Error!
    acceptsObject({}); // Works!
复制代码
可选的函数参数

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

    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!
复制代码

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

  • 布尔: true 或 false
  • 数字:像 42 或 3.14
  • 字符串:像 "foo" 或 "bar"
    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

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

    function stringify(value: mixed) {
      // $ExpectError
      return "" + value; // Error!
    }
    
    stringify("foo");
复制代码
    function stringify(value: mixed) {
      if (typeof value === 'string') {
        return "" + value; // Works!
      } else {
        return "";
      }
    }
    stringify("foo");
复制代码
任意类型 any

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

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

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

    function add(one: any, two: any): number {
      return one + two;
    }
    
    add(1, 2);     // Works.
    add("1", "2"); // Works.
    add({}, []);   // Works.
复制代码
接口类型 interface

以使用 interface 以声明期望的类的结构:

    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,希望类匹配一个接口。这可以防止编辑类时发生不兼容的更改。

    interface Serializable {
      serialize(): string;
    }
    
    class Foo implements Serializable {
      serialize() { return '[Foo]'; } // Works!
    }
    
    class Bar implements Serializable {
      // $ExpectError
      serialize() { return 42; } // Error!
    }
复制代码
数组类型 Array

要创建一个数组类型,可以使用 Array 类型,其中 Type 是数组中元素的类型

    let arr: Array<number> = [1, 2, 3];
复制代码

总结

Flow在开发过程中可以强制定义属性,在后期维护上可以大大节约维护成本。js作为弱类型语言确实再开发大型项目时,会出现奇奇怪怪的bug,所以加上静态校验是必不可少的。目前我司开发的项目,简单的基本上加上Flow,复杂重构的,已全部改为TypeScript来规避掉开发过程中声明不严谨的错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值