前端进阶(5) - js 扩大:静态范例搜检(facebook flow)

js 扩大:静态范例搜检(facebook flow)

js 言语与 java、C 系列等言语有一点很大的差别,就是 js 言语是弱范例言语。js 言语的这个特征能够让人人以为 js 很自在,没有强制性的束缚,然则当碰到大型项目的时刻,js 的这个特征就会变得比较贫苦,由于这会致使团队的代码很不可控。这个缘由也是促使 TypeScript 降生的一个很主要的缘由。

但实在许多开辟人员照样比较喜好用 js 来开辟项目,所以 facebook 开辟出 flow 来协助 js 言语扩大静态范例搜检功用,躲避上面提到的题目。

1. 代码示例

flow 划定,在需要做 ‘flow 静态范例搜检’ 文件的开首加上 // @flow 这段解释,让东西辨认这个文件需要做静态范例搜检,不然就会看成平常 js 文件看待,不做静态范例搜检。

flow 静态范例险些能够应用到一切的 js 对象,包括 es6 扩大的 class, module 等,也包括 jsx 语法。

以下是一些基础的静态范例举例,更细致的能够检察 Type Annotations | Flow.

1.1 基础范例

与 js 的基础数据范例相似,包括:

  • boolean: 对应 js 的 Boolean 范例
  • number: 对应 js 的 Number 范例
  • string: 对应 js 的 String 范例
  • null: 对应 js 的 null
  • void: 对应 js 的 undefined

一般的 js 代码

let hello = 'hello'; // 声明一个变量

hello = 2 * 2; // 从新赋值

hello = []; // 从新赋值

加上 flow 静态范例搜检扩大的代码

// @flow

let hello: string = 'hello'; // 声明一个 string 范例的变量

hello = 2 * 2; // 报错

hello = []; // 报错

hello = 'hi'; // 从新赋值

1.2 函数

一般的 js 代码

function plus(a, b) {
  return a + b;
}

plus(); // NaN
plus(1); // NaN
plus(1, 2); // 3
plus('hello'); // 'helloundefined'
plus('hello', ' hi'); // 'hello hi'
plus({}, {}); // '[object Object][object Object]'

加上 flow 静态范例搜检扩大的代码

// @flow

// 定义一个 '两个数字参数,返回值也是数字' 的函数
function plus(a: number, b: number): number {
  return a + b;
}

plus(); // 报错
plus(1); // 报错
plus('hello'); // 报错
plus('hello', ' hi'); // 报错
plus({}, {}); // 报错

plus(1, 2); // 3

1.3 能够(Maybe),可选(Optional),语义(Literal),夹杂(Mixed)

能够(Maybe)范例用一个 ? 在范例前面示意,包括范例自身、nullundefined

// @flow

let hello: ?string; // 声明一个数据范例能够是 string, null, undefined 的变量

hello = null; // 赋值
hello = undefined; // 从新赋值
hello = 'hello'; // 从新赋值
hello = 1; // 报错
hello = true; // 报错

可选(Optional)范例平常用于对象属性或许函数参数,在称号背面加一个 ?,包括范例自身、undefined

// @flow

const obj: {hello? : string}; // 属性 hello 能够是 string, undefined

obj = {}; // 赋值
obj = {hello: undefined}; // 从新赋值
obj = {hello: 'hello'}; // 从新赋值
obj = {hello: null}; // 报错
obj = {hello: 1}; // 报错
obj = {hello: true}; // 报错

// 属性 param 能够是 number, undefined
function method(param?: number) { /* ... */ }

method(); // 一般
method(undefined); // 一般
method(1.12); // 一般
method(null); // 报错
method('hello'); // 报错

语义(Literal)范例平常用于声明某个,某几个特定的值(多个值用 | 分开)

// @flow

let hello: 'hello'; // 声明一个只能赋值 'hello' 的变量

hello = 'hello'; // 赋值
hello = 'hi'; // 报错
hello = 12; // 报错
hello = undefined; // 报错
hello = null; // 报错

function method(param: 1 | 'hi' | boolean): void { /* ... */ }

method(); // 报错,缺乏参数
method(1); // ok
method(1.2); // 报错,范例不对
method('hi'); // ok
method('hello'); // 报错,范例不对
method(true); // ok
method(false); // ok

夹杂(Mixed)范例是指恣意数据范例

// @flow

let hello: mixed; // 声明一个 mixed 范例的变量

hello = 'hello'; // 赋值
hello = 'hi'; // 从新赋值
hello = 12; // 从新赋值
hello = undefined; // 从新赋值
hello = null; // 从新赋值

1.4 复合范例

数组

// @flow

let arr1: Array<boolean> = [true, false, true]; // 声明一个元素是 boolean 的数组
arr1 = [true, 1]; // 报错,1 不是 boolean 值
arr1 = ['']; // 报错,'' 不是 boolean 值

let arr2: Array<string> = ["A", "B", "C"]; // 声明一个元素是 string 的数组

let arr3: Array<mixed> = [1, true, "three"] // 声明一个元素是恣意范例的数组
arr1 = [true, 1]; // 从新赋值 
arr1 = ['']; // 从新赋值

map

// @flow

// 声明一个 map 范例,其有一个名为 foo,范例 boolean 的子元素
let obj1: { foo: boolean } = { foo: true };
obj1 = {}; // 报错,缺乏 foo 这个属性值
obj1 = {foo: 1}; // 报错,属性值 foo 的范例必需是 boolean
obj1 = {foo: false, bar: 'hello'}; // 从新赋值

// 声明一个 map 范例,其有名为 foo, bar, baz,范例 number, boolean, string 的子元素
let obj2: {
  foo: number,
  bar: boolean,
  baz: string,
} = {
  foo: 1,
  bar: true,
  baz: 'three',
};

更静态范例能够检察 Type Annotations | Flow.

2. 运用东西

装置

# 全局装置
npm i -g flow-bin

# 当地装置
npm i -D flow-bin

运用

flow init                       # 初始化项目

flow check path/to/dir          # 搜检这个目录下一切的文件
flow check path/to/js/file      # 搜检指定文件

3. 合营 babel 一同运用

由于 flow 静态范例只是对 js 的扩大,并非 js 原生支撑的,也不能直接运转,所以,平常 flow 都是合营 babel 一同运用的,如许就可以够在顺序运转的时刻举行静态范例搜检,到达我们想要的结果。

3.1 babel-preset-flow

装置 babel-preset-flow,如许 babel 在转码 js 文件时就可以辨认 flow 的语法。

npm i -D babel-preset-flow

.babelrc

{
  "presets": ["flow"]
}

源文件(flow)

// @flow

// 定义一个 '两个数字参数,返回值也是数字' 的函数
function plus(a: number, b: number): number {
  return a + b;
}

plus(); // 报错
plus(1); // 报错
plus('hello'); // 报错
plus('hello', ' hi'); // 报错
plus({}, {}); // 报错

plus(1, 2); // 3

转码后的文件

// 定义一个 '两个数字参数,返回值也是数字' 的函数
function plus(a, b) {
  return a + b;
}

plus(); // 报错
plus(1); // 报错
plus('hello'); // 报错
plus('hello', ' hi'); // 报错
plus({}, {}); // 报错

plus(1, 2); // 3

3.2 babel-plugin-flow-runtime

平常会在开辟环境下,运用 babel-plugin-flow-runtime 插件,如许就可以够在开辟的时刻,及时搜检数据范例,就像原生的运转 flow 静态范例搜检一样。(平常在产品环境不会运用这个功用,由于会分外斲丧 js 的机能)

npm i -D babel-plugin-flow-runtime flow-runtime

.babelrc

{
  "presets": ["flow"],
  "plugins": ["flow-runtime"]
}

源文件(flow)

// @flow

// 定义一个 '两个数字参数,返回值也是数字' 的函数
function plus(a: number, b: number): number {
  return a + b;
}

plus(); // 报错
plus(1); // 报错
plus('hello'); // 报错
plus('hello', ' hi'); // 报错
plus({}, {}); // 报错

plus(1, 2); // 3

转码后的文件

import t from 'flow-runtime';


// 定义一个 '两个数字参数,返回值也是数字' 的函数
function plus(a, b) {
  return a + b;
}

t.annotate(plus, t.function(t.param('a', t.number()), t.param('b', t.number()), t.return(t.number())));
plus(); // 报错
plus(1); // 报错
plus('hello'); // 报错
plus('hello', ' hi'); // 报错
plus({}, {}); // 报错

plus(1, 2); // 3

这个时刻,js 文件就会导入 flow-runtime 模块,对 plus 函数的参数 a, b 和返回值举行数据范例搜检,假如不符合数据定义,就会报错。

4. 后续

更多博客,检察 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版权声明:自在转载-非商用-非衍生-坚持签名(创意同享3.0许可证

    原文作者:senntyou
    原文地址: https://segmentfault.com/a/1190000016127186
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞