认识 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= [1, 2, 3];复制代码
总结
Flow
在开发过程中可以强制定义属性,在后期维护上可以大大节约维护成本。js作为弱类型语言确实再开发大型项目时,会出现奇奇怪怪的bug,所以加上静态校验是必不可少的。目前我司开发的项目,简单的基本上加上Flow
,复杂重构的,已全部改为TypeScript
来规避掉开发过程中声明不严谨的错误。