【前端】关于Flow中的联结类型如何使用
创始人
2024-02-09 04:17:43

在Flow 的使用入门一文中,我们在语法层面简要介绍了 Flow 的实际使用;其中有一个联结类型,本文介绍联结类型的使用及相关知识点。

联结类型简介

「联结类型」是笔者的翻译,Flow 文档中的原名是叫「Union Type」;意即多个类型联结在一起。不过我也有看到译文称为「联合类型」的;译名暂时不重要,确保我们讨论的是同一个东西就行。

联结类型的书写方式是多个类型用 | 符号联结在一起,如 type A = 1 | 2 | 3;不过如果类型名称较长,也可以分多行来写,每一个类型一行,注意:每个类型名前都要有 | 符号。

// @flow
type T = | string| number

联结类型的使用

联结类型可以将多个任意类型联结在一起,比如:

// @flow
type A = 1 | 2 | 3;
type B = number | 'a' | boolean;
type C = 'a' | 1 | true | Array;
type D = A | B | C;

如代码所示,可以是多个字面量值类型的联结,多个基本类型的联结,甚至是多个联结类型的再次联结。从语义上联结类型可以理解成「或」;如类型为 A 的变量值可以是 1 或者 2 或者 3;是一个允许的类型的集合。

既然提出了集合的概念,就有子集。在 Flow 的联合类型中;如果某个联结类型 T 满足某一约束要求,那么类型 T 的子集构成的联结类型,也满足这一约束要求;可以说,针对两个联结类型 A 和 B,如果 A 是 B 的子集,那么 A 是 B 的子类型。

// @flow
type Base = 1 | 2 | 3 | 4 | 5;
type Sub = 1 | 2 | 3;
function myMethod(arg: Base) {// ... code here
}
let a: Sub = 1;
myMethod(a);

联结类型的细化

函数参数接受一个联结类型是很常用的;比如 jQuery:既可以接受一个 CSS 选择符(string 类型),也可以接受一个函数(Function 类型);如果要给 jQuery 的参数写一个类型定义,大概是这样:

// @flow
type T = string | Function;
function jQuery(arg: T) {// ...
}
jQuery('div');
jQuery(function(){// ...
})

Flow 要求,如果一个函数接受一个联结类型,传入的参数类型可以是类型中的任意一个(one of those types);但是函数内部,必须对联结类型的每一种情况进行处理(all of the possible types);在 Flow 的官网中,把这个原则称为 requires one in, but all out ,而这个过程称为 类型的细化(Refinements) 。

// @flow
function myMethod(value: string | number | boolean){if (typeof value === 'string') {// Block A} else if (typeof value === 'number') {// Block B} else {// Block C}
}

在上述代码中,定义 value 类型是 string、number 和 boolean 的联结类型;因此在函数内部对 value 进行处理时,必须进行区分;只有 value 是 string 类型值才会运行到 A 代码块;只有 value 是 number 类型值才会运行到 B 代码块;而在 C 代码块中,已经把联结类型中的 string 和 number 都排除了,所以这里可以安全得把 value 作为 boolean 类型处理。

这样通过细化处理把联结类型拆解逐一处理,可以确保联结类型的安全使用。

不过有一点神奇的是:在处理联结类型后,如果所有的可能类型都判断并处理了,并且还有其他的逻辑分支,Flow 是不做检查的。

// @flow
function myMethod(value: string | number ) {if(typeof value === 'string') {// Block A;var a: string = value;} else if(typeof value === 'number') {// Block B;var b: number = value;} else {// Block C;var c: Object = value;var d: Array<*> = value.push('a');var e: number = value - 1;}
}

在代码块 C 中,对 value 进行的操作肯定是有冲突的;但是运行 Flow 却不会报错,因为 Flow 聪明的知道:在 A 和 B 代码块中,已经处理了 value 所有的可能性,程序运行是无法到达 C 代码块的。

Flow 检查器必须知道每一个代码块的可到达性,以及运行到达这段代码块时变量的类型可能;这称为静态类型检查器的可到达性分析( reachability analysis )。

互斥类型的细化

在之前的例子中,我们是通过 JavaScript 语言中的 typeof 来判断变量到底是属于联结类型中的哪一个。但是在很多情况下,我们没法使用 typeof 来进行;比如多个对象类型的联结,那又如何处理呢?

// @flow
type A = {name: string,
}
type B = {age: number,
}
type C = A | B;

类型 C 是 A 和 B 构成的联结类型;A 定义的类型,即要求该类型的变量,必须有 name 属性,取值是 string 类型(类似于 TypeScript 中的 Interface)。在 JavaScript 的代码中,区分 A、B 两种类型肯定能不能用 typeof,因为都是对象;针对这种类型,Flow 提供两种方案。

一、互斥类型

// @flow
type Success = {success: true,value: boolean,
};
type Failure = {success: false,error: string
}
type Response = Success | Failure;function handleResponse(res: Response) {if(res.success){var a: boolean = res.value;} else {var b: string = res.error;}
}

类型 Success 和 Failure 都有一个 同名的属性 (success),且定义的类型是一个 精确的字面量类型 ;Flow 将这样的多个对象类型称为互斥类型( disjoint unions )。他们之间就是根据这个同名的属性进行区分的。

如果刚好你的业务数据中有可以用来区分的同名字段,可以使用互斥类型,否则还需要为了适应 Flow 的要求去修改业务数据结构。

二、确定结构的对象类型( exact object type )

使用对象类型,我们只能限制对象中必须有哪些属性,这之外还有其他哪些属性是不限制的;如果我们能精确的定义,接受的对象类型参数只能有哪些属性,那就可以用 确定结构的对象类型 来做类型标注。 确定结构的对象类型 是在定义对象类型的时候,在大括号内部加上一对 | 符号,比如:

// @flow
type T = {|name: string,age: number
|}

这个类型的变量有且只有这两个属性。如果是两个这样的 确定结构的对象类型 构成的联结类型,在使用时根据各自独有的属性就可以进行区分了。

理想的情况是这么用的:

// @flow
type A = {|name: string
|}
type B = {|age: number
|}
type C = A | B;function myMethod(value: C) {if (typeof value.name === 'string') {var a: string = value.name;} else {var b: number = value.age;}
}
// 这个例子 Flow 检查是会出错的!!!

不过遗憾的是,Flow 当前(v0.42.0)对此的实现是不太完善的。

1. 从代码逻辑来讲,如果限制了参数 value 的类型必须是 C 的话,那么在 else 代码块里,可以确定 value 的类型只能是 B;但是 Flow 没有推断出来。如果你在使用中碰到这样的坑,目前可以这么修改你的代码使其通过 Flow 的检查。

// @flow
type A = {|name: string
|}
type B = {|age: number
|}
type C = A | B;function myMethod(value: C) {if (typeof value.name === 'string') {var a: string = value.name;} else if(typeof value.age === 'number') {var b: number = value.age;}
}

2.

把上述代码中的 typeof 判断类型,改成用 in 操作符判断属性存在,符合 JavaScript 的语言逻辑,但是通不过 Flow 的检查。

3. 如果直接用 if(o.p) 来区分,能通过 Flow 的检查,但是从 JavaScript 语言逻辑来讲是不正确的;比如判断一个数字类型变量是否存在,数字为 0 时结果就不正确。

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...