ユニオン
他の型のいずれかである型を作成することが役立つ場合があります。たとえば、一連のプリミティブ値型を受け入れる関数を記述する場合があります。このため、Flowはユニオン型をサポートしています。
1function toStringPrimitives(value: number | boolean | string): string {2 return String(value);3}4
5toStringPrimitives(1); // Works!6toStringPrimitives(true); // Works!7toStringPrimitives('three'); // Works!8
9toStringPrimitives({prop: 'val'}); // Error! 10toStringPrimitives([1, 2, 3, 4, 5]); // Error!
9:20-9:32: Cannot call `toStringPrimitives` with object literal bound to `value` because: [incompatible-call] Either object literal [1] is incompatible with number [2]. Or object literal [1] is incompatible with boolean [3]. Or object literal [1] is incompatible with string [4].10:20-10:34: Cannot call `toStringPrimitives` with array literal bound to `value` because: [incompatible-call] Either array literal [1] is incompatible with number [2]. Or array literal [1] is incompatible with boolean [3]. Or array literal [1] is incompatible with string [4].
ユニオン型の構文
ユニオン型は、垂直バー `|` で結合された任意の数の型です。
Type1 | Type2 | ... | TypeN
複数の行にユニオン型を分割する場合に便利な先頭の垂直バーを追加することもできます。
type Foo =
| Type1
| Type2
| ...
| TypeN
ユニオン型のメンバーは、別のユニオン型も含め、あらゆる型にすることができます。
1type Numbers = 1 | 2;2type Colors = 'red' | 'blue'3
4type Fish = Numbers | Colors;
Flow Enumを有効にしている場合、リテラル型のユニオンの代替手段となる可能性があります。
ユニオンの省略記法
ある型 `T` と `null` または `void` のユニオンは一般的であるため、`?` プレフィックスを使用してMaybe型と呼ばれる省略記法を提供しています。型 `?T` は `T | null | void` と同等です。
1function maybeString(x: ?string) { /* ... */ }2maybeString('hi'); // Works!3maybeString(null); // Works!4maybeString(undefined); // Works!
存在するすべての型のユニオンは、`mixed`型です。
1function everything(x: mixed) { /* ... */ }2everything(1); // Works!3everything(true); // Works!4everything(null); // Works!5everything({foo: 1}); // Works!6everything(new Error()); // Works!
ユニオンと絞り込み
ユニオン型の値がある場合、それを分割して個々の型を個別に処理することがよくあります。Flowのユニオン型では、値を単一の型に絞り込むことができます。
たとえば、`number`、`boolean`、または`string`であるユニオン型の値がある場合、JavaScriptの`typeof`演算子を使用して、数値の場合を個別に処理できます。
1function toStringPrimitives(value: number | boolean | string) {2 if (typeof value === 'number') {3 return value.toLocaleString([], {maximumSignificantDigits: 3}); // Works!4 }5 // ...6}
`typeof`で値を確認し、数値であるかどうかをテストすることで、Flowはそのブロック内では数値のみであることを認識します。その後、そのブロック内で値を数値として扱うコードを記述できます。
ユニオン型は1つの入力、しかしすべての出力が必要
ユニオン型を受け入れる関数を呼び出すときは、**それらの型のいずれか1つ**を渡す必要があります。しかし、関数内では、**考えられるすべての型**を処理する必要があります。
絞り込みを使用して、各型を個別に処理するように関数を書き直してみましょう。
1function toStringPrimitives(value: number | boolean | string): string {2 if (typeof value === 'number') {3 return String(value);4 } else if (typeof value === 'boolean') {5 return String(value);6 }7 return value; // If we got here, it's a `string`!8}
考えられるすべての型を処理しないと、Flowからエラーが発生します。
1function toStringPrimitives(value: number | boolean | string): string {2 if (typeof value === 'number') {3 return String(value);4 }5 return value; // Error! 6}
5:10-5:14: Cannot return `value` because boolean [1] is incompatible with string [2]. [incompatible-return]
分離オブジェクトユニオン
Flowには、「分離オブジェクトユニオン」として知られる特殊なユニオン型があり、絞り込みで使用できます。これらの分離オブジェクトユニオンは、それぞれが単一のプロパティでタグ付けされた任意の数のオブジェクト型で構成されています。
たとえば、リクエストを送信した後にサーバーからのレスポンスを処理する関数があるとします。リクエストが成功すると、`type`プロパティが`'success'`に設定され、更新された`value`を持つオブジェクトが返されます。
{type: 'success', value: 23}
リクエストが失敗すると、`type`が`'error'`に設定され、エラーを記述する`error`プロパティを持つオブジェクトが返されます。
{type: 'error', error: 'Bad request'}
これらのオブジェクトの両方を単一のオブジェクト型で表現しようとすると、Flowでは`type`プロパティに基づいてプロパティが存在することがわかっているにもかかわらず、認識されないという問題が発生します。
1type Response = {2 type: 'success' | 'error',3 value?: number,4 error?: string5};6
7function handleResponse(response: Response) {8 if (response.type === 'success') {9 const value: number = response.value; // Error! 10 } else {11 const error: string = response.error; // Error! 12 }13}
9:27-9:40: Cannot assign `response.value` to `value` because undefined [1] is incompatible with number [2]. [incompatible-type]11:27-11:40: Cannot assign `response.error` to `error` because undefined [1] is incompatible with string [2]. [incompatible-type]
これら2つの異なる型を1つにまとめようとすると、問題が発生するだけです。
代わりに、両方のオブジェクト型のユニオン型を作成すると、Flowは`type`プロパティに基づいてどのオブジェクトを使用しているかを認識できます。
1type Response =2 | {type: 'success', value: 23}3 | {type: 'error', error: string};4
5function handleResponse(response: Response) {6 if (response.type === 'success') {7 const value: number = response.value; // Works!8 } else {9 const error: string = response.error; // Works!10 }11}
このパターンを使用するには、ユニオン内のすべてのオブジェクトにあるキー(上記の例では`type`)が存在する必要があり、すべてのオブジェクトはそのキーに対して異なるリテラル型を設定する必要があります(上記の例では、文字列`'success'`と文字列`'error'`)。数値やブール値など、任意のリテラル型を使用できます。
完全一致オブジェクト型を持つ分離オブジェクトユニオン
分離ユニオンでは、各オブジェクト型を区別するために単一のプロパティを使用する必要があります。異なるプロパティによって、2つの異なる不完全オブジェクトを区別することはできません。
1type Success = {success: true, value: boolean, ...};2type Failed = {error: true, message: string, ...};3
4function handleResponse(response: Success | Failed) {5 if (response.success) {6 const value: boolean = response.value; // Error! 7 }8}
6:37-6:41: Cannot get `response.value` because property `value` is missing in object type [1]. [prop-missing]
これは、Flowでは、幅部分型付けのために、不完全オブジェクト型が期待するよりも多くのプロパティを持つオブジェクト値を渡しても問題ないためです。
1type Success = {success: true, value: boolean, ...};2type Failed = {error: true, message: string, ...};3
4function handleResponse(response: Success | Failed) {5 // ...6}7
8handleResponse({9 success: true,10 error: true,11 value: true,12 message: 'hi'13});
オブジェクトが何らかの形で互いに競合しない限り、それらを区別する方法はありません。
ただし、これを回避するために、**完全一致オブジェクト型**を使用できます。
1type Success = {success: true, value: boolean};2type Failed = {error: true, message: string};3
4type Response = Success | Failed;5
6function handleResponse(response: Response) {7 if (response.success) {8 const value: boolean = response.value;9 } else {10 const message: string = response.message;11 }12}
完全一致オブジェクト型では、追加のプロパティを持つことができないため、オブジェクトは互いに競合し、どちらがどちらであるかを区別できます。