## Maybe(可能)類型
Maybe類型用一個`?`在類型前面表示,包含類型本身、`null`、`undefined`。
```
// @flow
/* demo1 */
let hello: ?string; // 聲明一個數據類型可以是 string, null, undefined 的變量
hello = null; // 賦值
hello = undefined; // 重新賦值
hello = 'hello'; // 重新賦值
hello = 1; // 報錯!
hello = true; // 報錯!
/* demo2 */
function acceptsMaybeString(value: ?string) {
// ...
}
acceptsMaybeString("bar"); // 成功!
acceptsMaybeString(undefined); // 成功!
acceptsMaybeString(null); // 成功!
acceptsMaybeString(); // 成功!
```
</br>
## Optional(可選)類型
>Optional一般用于對象屬性或者函數參數,在名稱后面加一個`?`,包含類型本身、`undefined`,注意,這里不包括`null`類型。
**可選的對象屬性**
即對象類型可以具有可選屬性,問號`?`位于屬性名稱后面。
```
{ propertyName?: string }
```
除了它們的設定值類型之外,這些可選屬性也可以被`void`完全省略。但是不能是`null`。
```
// @flow
function acceptsObject(value: { foo?: string }) {
// ...
}
acceptsObject({ foo: "bar" }); // 成功!
acceptsObject({ foo: undefined }); // 成功!
acceptsObject({ foo: null }); // 報錯!
acceptsObject({ foo: true}); // 報錯!
acceptsObject({}); // 成功!
```
**可選的函數參數**
即函數可以具有可選參數,其中問號`?`出現在參數名稱后面。同樣,該參數不能為`null`。
```
// @flow
function acceptsOptionalString(value?: string) {
// ...
}
acceptsOptionalString("bar"); // 成功!
acceptsOptionalString(undefined); // 成功!
acceptsOptionalString(null); // 報錯!
acceptsOptionalString(); // 成功!
```
</br>
## Literal(字面文字)類型
>字面文字類型指的是以真實值作為數據類型,可用的值有三種,即數字、字符串或布爾值。
```
// @flow
/* demo1 */
let hello: 'hello'; // 聲明一個只能賦值 'hello' 的變量
hello = 'hello'; // 賦值成功!
hello = 'hi'; // 報錯!
hello = 12; // 報錯!
hello = undefined; // 報錯!
hello = null; // 報錯!
/* demo2 */
function method(param: 1 | 'hi' | boolean): void { /* ... */ }
method(); // 報錯,缺少參數
method(1); // 成功!
method(1.2); // 報錯,類型不對
method('hi'); // 成功!
method('hello'); // 報錯,類型不對
method(true); // 成功!
method(false); // 成功!
```
</br>
## Mixed(混合)類型
Mixed類型是指任意數據類型。
```
// @flow
let hello: mixed; // 聲明一個 mixed 類型的變量
hello = 'hello'; // 賦值
hello = true; // 重新賦值
hello = 12; // 重新賦值
hello = undefined; // 重新賦值
hello = null; // 重新賦值
```
有時候我們并不能確定需要的值到底是哪種類型,這時候我們可以使用混合類型來表示,但在使用該值之前,我們需要判斷該值到底是哪種類型,否則會引起錯誤。
```
// @flow
function stringify(value: mixed) {
return "" + value;
}
stringify("foo");
```
如上面代碼,運行之后就報錯了,
> Error ----------------------------------------------------------------------------------------------- src/index.js:12:10
> Cannot add empty string and `value` because mixed [1] could either behave like a string or like a number.
> src/index.js:12:10
> 12| return "" + value; // Error!
> ^^^^^^^^^^
>References:
> src/index.js:10:27
> 10| function stringify(value: mixed) {
> ^^^^^ [1]
>Found 1 error
原因是雖然輸入時可以用`mixed`,但Flow會認為函數中`value`的值不見得可以與`string`類型作相加,所以會請求你要在函數中的代碼,要加入檢查對傳入類型在運行期間的類型檢查代碼,例如像下面修改過才能過關:
```
// @flow
function stringify(value: mixed) {
if (typeof value === 'string' || typeof value === 'number') {
return "" + value;
} else {
return value;
}
}
stringify("foo");
```
從上面的例子可以看到Flow除了對類型會作檢查外,它也會請求對某些類型需要有動態的檢查。在官方的文件可以參考[Type Refinements](https://flow.org/en/docs/lang/refinements/#_)這個章節。
</br>
## Any(任意)類型
如果你想要一種方法來選擇不使用類型檢查器,或是還在開發中正在調試時,有一種作為漸進的改善代碼的類型。
`Any`類型就可以做到這一點。但是切記,使用`Any`是不安全的,因為會失去類型檢查,應盡可能避免使用。
例如,下面的代碼不會報告任何提示:
```
// @flow
function add(one: any, two: any): number {
return one + two;
}
add(1, 2); // 成功!
add("1", "2"); // 成功!
add("1", 2); // 成功!
add({}, []); // 成功!
```
當然還不止上面的問題,使用`Any`類型還可能會出現即使是會導致運行時錯誤的代碼也不會被Flow捕獲。
```
// @flow
function getObjProperty(obj: any) {
return obj.a.b;
}
getObjProperty({}); // No Errors !
```
上面這段代碼明顯錯誤(Uncaught TypeError: Cannot read property 'b' of undefined),但是由于`Any`類型的存在,Flow并沒有捕捉到錯誤,反而通過了檢查。
</br>
## Type Alias(類型別名)
`Type Alias`提供了可以預先定義與集中代碼中所需要的類型,一個簡單的例子如下:
```
// @flow
type T = Array<string>;
let x: T = [];
x["Hi"] = 2; // 報錯!
```
`Type Alias`也可以用于復雜的應用情況,詳見Flow官網提供的[Type Aliases](https://flowtype.org/docs/type-aliases.html)這一章節。
## Union(聯合)類型
所有的類型都可以使用垂直線符號`|`作為聯合使用(也就是 OR 的意思),例如`string | number`指的是兩種類型其中一種都可使用,這是一種聯合的類型,稱為`Union`類型。
```
// @flow
type U = number | string;
let x: U = 1; // 成功!
x = "two"; // 成功!
x = true; // 報錯!
```
</br>