## Array(数组)类型
要创建一个数组类型,可以使用`Array<Type>`类型,其中 Type 是数组中元素的类型。例如,为你使用的数字数组创建一个类型`Array<number>`,这样子就会限定数组中的值只能使用数字的数据类型。当然你也可以加入埀直线(|)来定义允许多种类型,例如`Array<number|string>`。
```
// @flow
let arr1: Array<number> = [1, 2, 3]; // 成功!
let arr2: Array<number> = [1, '2', true]; // 报错!'2' 和 true 并非是number类型。
let arr3: Array<number | string | boolean> = [1,'2',true]; // 成功!
let arr4: Array<mixed> = [1,'2',true]; // 声明一个元素是任意类型的数组
arr4 = [true, 1]; // 重新赋值
arr4 = ['']; // 重新赋值
```
暂时就介绍这么多,还有一些类型文章中没有提到,更多更详细的内容请在Flow官网的[Array Types](https://flow.org/en/docs/types/arrays/)章节中查看。
</br>
## Object(对象)类型
对象类型会尝试尽可能多地匹配JavaScript中对象的语法,也是使用大括号`{}`和`key:value`这样的键值对来表示,用`,`隔开各个键值对。
```
// @flow
var obj1: { foo: boolean } = { foo: true };
var obj2: {
foo: number,
bar: boolean,
baz: string,
} = {
foo: 1,
bar: true,
baz: 'three',
};
```
**可选的对象属性**
即对象类型可以具有可选属性。
在JavaScript中,访问不存在的属性,返回的结果为`undefined`。这是JavaScript程序中常见的错误,因此Flow将这些错误转换为类型错误。
```
// @flow
var obj = { foo: "bar" };
obj.bar;
```
运行一下FLow,显示的结果如下,
>Error ------------------------------------------------------------------------------------------------- src/index.js:4:1
>Cannot get `obj.bar` because property `bar` is missing in object literal [1].
> src/index.js:4:1
> 4| obj.bar;
^^^^^^^
>References:
> src/index.js:2:11
> 2| var obj = { foo: "bar" };
^^^^^^^^^^ [1]
>Found 1 error
如果对象有时没有属性,可以通过添加问号`?`使其成为可选属性,问号`?`位于属性名称后面,`{属性名称?: 类型}`。
```
// @flow
var obj: { foo?: boolean } = {};
obj.foo = true; // 成功!
obj.foo = 'hello'; // 报错!
```
值得注意的是,可选属性值可以是`void`或省略(什么都不写),但是不能是`null`。
```
// @flow
function acceptsObject(value: { foo?: string }) {
// ...
}
acceptsObject({ foo: "bar" }); // 成功!
acceptsObject({ foo: undefined }); // 成功!
acceptsObject({}); // 成功!
acceptsObject({ foo: null }); // 报错!
```
### 对象类型推导
Flow有两种不同的方式推导出对象字面量的类型。
#### Sealed objects(密封的对象)
在Flow中创建一个密封的对象类型的方法是创建一个带有属性的对象。密封的对象知道你声明的所有属性及其值的类型。
```
// @flow
var obj = {
foo: 1,
bar: true,
baz: 'three'
};
var foo: number = obj.foo; // 成功!
var bar: boolean = obj.bar; // 成功!
var baz: null = obj.baz; // 报错!
var bat: string = obj.bat; // 报错!
```
Flow不允许你为密封对象添加新的属性。
```
// @flow
var obj = {
foo: 1
};
obj.bar = true; // 报错!
obj.baz = 'three'; // 报错!
```
这里的解决方法是将对象转换为未密封的对象。
#### Unsealed objects(未密封的对象)
在Flow中创建一个未密封的对象类型的方法是创建一个带没有属性的对象。未密封的对象不知道你声明的所有属性及其值的类型,并且允许你为其添加新的属性。
```
// @flow
var obj = {};
obj.foo = 1; // 成功!
obj.bar = true; // 成功!
obj.baz = 'three'; // 成功!
var foo: number = obj.foo; // 成功!
```
#### 为未密封对象属性重新赋值
与`var`和`let`变量相似,你可以改变未密封的对象的属性值。Flow会为你设置可能的类型。
```
// @flow
var obj = {};
if (Math.random()) obj.prop = true;
else obj.prop = "hello";
var val1: boolean = obj.prop; // 报错!
var val2: string = obj.prop; // 报错!
var val3: boolean | string = obj.prop; // 成功!
var val4: boolean | string | number = obj.prop; // 成功!
var val5: mixed = obj.prop; // 成功!
```
上一章节说过,Flow会对代码进行动态检查,由于obj.prop的类型可能为`boolean`或者`string`,所以接收改属性的变量类型也应该是(至少)能够接收这两种类型的变量,给单一类型的变量的赋值是会失败的。
当然,当Flow可以确定重新分配后的属性类型时,Flow会为其分配确定的类型。
```
// @flow
var obj = {};
obj.prop = true;
obj.prop = "hello";
var val1: boolean = obj.prop; // 报错!
var val2: string = obj.prop; // 成功!
```
#### 未密封对象上的未知属性查找是不安全的
未密封的对象允许随时写入新的属性。Flow确保读取与写入兼容,但不能确保写入发生在读取之前(按执行顺序)。**这意味着Flow对于从未密封对象中读取未知属性查,并且进行其它写入的这种行为是不做检查的。**
```
var obj = {};
obj.foo = 1;
obj.bar = true;
var foo: number = obj.foo; // 成功!
var bar: boolean = obj.bar; // 成功!
var baz: string = obj.baz; // 成功!?这里确实是成功了。
```
*Flow的这种不安全行为,未来可能被改进。*
### 确切的对象类型
在Flow中,在预期正常对象类型的情况下传递具有额外属性的对象是安全的。
```
// @flow
function method(obj: { foo: string }) {
// ...
}
method({
foo: "test",
bar: 42
});
```
上面的这种写法是允许的,也是安全的。
> 这是一种通常被称为[“宽度子类型”](https://link.jianshu.com/?t=https%3A%2F%2Fflow.org%2Fen%2Fdocs%2Flang%2Fwidth-subtyping%2F)的子类型,因为“更宽”(即具有更多属性)的类型是更窄类型的子类型。
不过,有时候你的代码就只需要传入你想要的类型数据,并不想传入一些不必要的数据,那么这时候阻止这种行为并且只允许一组特定的属性是必要的。为此,Flow有了以下的做法,
```
{| foo: string, bar: number |}
```
与常规对象类型不同,将具有“额外”属性的对象传递给确切的对象类型是无效的。
```
// @flow
var foo: {| foo: string |} = { foo: "Hello", bar: "World!" }; // 报错!
```
> Error ------------------------------------------------------------------------------------------------ src/index.js:2:30
> Cannot assign object literal to `foo` because property `bar` is missing in object type [1] but exists in object
literal [2].
> src/index.js:2:30
> 2| var foo: {| foo: string |} = { foo: "Hello", bar: "World!" };
^^^^^^^^^^^^^^^^^^^^^^^^^ [2]
>References:
> src/index.js:2:10
> 2| var foo: {| foo: string |} = { foo: "Hello", bar: "World!" };
^^^^^^^^^^^^^ [1]
>Found 1 error
</br>
关于`Object`类型,更多更详细的内容请在Flow官网的[Object Types](https://flow.org/en/docs/types/objects/)章节中查看。