## 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/)章節中查看。