配列
配列型は、すべての要素が同じ型を持つ長さが不明なリストを表します。これは、長さが固定されており、各要素が異なる型を持つことができるタプル型とは対照的です。
JavaScriptの配列リテラル値は、タプル型と配列型の両方を作成するために使用できます。
1const arr: Array<number> = [1, 2, 3]; // As an array type2const tup: [number, number, number] = [1, 2, 3]; // As a tuple type
Array
型
型Array<T>
は、型T
の要素の配列を表します。たとえば、数値の配列はArray<number>
になります。
1const arr: Array<number> = [1, 2, 3];
Array<T>
の中には、任意の型を入れることができます。
1const arr1: Array<boolean> = [true, false, true];2const arr2: Array<string> = ["A", "B", "C"];3const arr3: Array<mixed> = [1, true, "three"];
$ReadOnlyArray
型
Array<T>
の代わりに型$ReadOnlyArray<T>
を使用して、変更できない読み取り専用の配列を表すことができます。読み取り専用の配列に直接書き込むことはできず、.push()
、.unshift()
などの配列を変更するメソッドを使用することはできません。
1const readonlyArray: $ReadOnlyArray<number> = [1, 2, 3]2
3const first = readonlyArray[0]; // OK to read4readonlyArray[1] = 20; // Error! 5readonlyArray.push(4); // Error! 6readonlyArray.unshift(4); // Error!
4:1-4:16: Cannot assign `20` to `readonlyArray[1]` because read-only arrays cannot be written to. [cannot-write]5:15-5:18: Cannot call `readonlyArray.push` because property `push` is missing in `$ReadOnlyArray` [1]. [prop-missing]6:15-6:21: Cannot call `readonlyArray.unshift` because property `unshift` is missing in `$ReadOnlyArray` [1]. [prop-missing]
型$ReadOnlyArray<T>
の配列は、変更可能な要素を持つことができます。
1const readonlyArray: $ReadOnlyArray<{x: number}> = [{x: 1}];2readonlyArray[0] = {x: 42}; // Error! 3readonlyArray[0].x = 42; // Works
2:1-2:16: Cannot assign object literal to `readonlyArray[0]` because read-only arrays cannot be written to. [cannot-write]
Array
の代わりに$ReadOnlyArray
を使用する主な利点は、$ReadOnlyArray
の型パラメーターが共変であるのに対し、Array
の型パラメーターは不変であることです。つまり、$ReadOnlyArray<number>
は$ReadOnlyArray<number | string>
のサブタイプですが、Array<number>
はArray<number | string>
のサブタイプではありません。したがって、さまざまな型の要素を持つ配列の型注釈に$ReadOnlyArray
を使用すると便利なことがよくあります。たとえば、次のシナリオを考えてみましょう。
1const someOperation = (arr: Array<number | string>) => {2 // Here we could do `arr.push('a string')`3}4
5const array: Array<number> = [1];6someOperation(array) // Error!
6:15-6:19: Cannot call `someOperation` with `array` bound to `arr` because string [1] is incompatible with number [2] in array element. Arrays are invariantly typed. See https://flow.dokyumento.jp/en/docs/faq/#why-cant-i-pass-an-arraystring-to-a-function-that-takes-an-arraystring-number. [incompatible-call]
someOperation
関数のパラメーターarr
が変更可能なArray
として型付けされているため、そのスコープ内で文字列をプッシュすることが可能になり、外部のarray
変数の型契約を破ることになります。この場合、パラメーターを$ReadOnlyArray
として注釈することで、Flowはこの問題が発生しないことを確認でき、エラーは発生しません。
1const someOperation = (arr: $ReadOnlyArray<number | string>) => {2 // Nothing can be added to `arr`3}4
5const array: Array<number> = [1];6someOperation(array); // Works
$ReadOnlyArray<mixed>
は、すべての配列とすべてのタプルのスーパータイプを表します。
1const tup: [number, string] = [1, 'hi'];2const arr: Array<number> = [1, 2];3
4function f(xs: $ReadOnlyArray<mixed>) { /* ... */ }5
6f(tup); // Works7f(arr); // Works
空の配列リテラル
空の配列リテラル([]
)は、注釈要件に関してFlowで特別に処理されます。これらが特別なのは、型を決定するのに十分な情報が含まれていないと同時に、それらの即時コンテキストで常に型注釈を必要とするには一般的すぎるためです。
したがって、空の配列を型付けするために、Flowは次のルールに従います。
文脈推論
まず、文脈情報が存在する場合は、それを使用して配列要素の型を決定します。
1function takesStringArray(x: Array<string>): void {}2
3const arr1: Array<string> = [];4takesStringArray([]);
どちらの場合も、[]
はArray<string>
として型付けされます。
文脈情報が機能するためには、配列の定義時に型が利用可能である必要があることに注意してください。これは、次のコードの最後の2行がエラーになることを意味します。
1function takesStringArray(x: Array<string>): void {}2
3const arr2 = []; 4takesStringArray(arr2);
3:7-3:10: Cannot determine type of empty array literal. Please provide an annotation. [missing-empty-array-annot]4:18-4:21: Cannot call `takesStringArray` with `arr2` bound to `x` because string [1] is incompatible with unknown element of empty array [2] in array element. Arrays are invariantly typed. See https://flow.dokyumento.jp/en/docs/faq/#why-cant-i-pass-an-arraystring-to-a-function-that-takes-an-arraystring-number. [incompatible-call]
2番目のエラーは、arr2
がArray<empty>
として推論され、それがtakesStringArray
の呼び出しで別のエラーにつながるためです。
初期化子推論
Flowでは、空の配列が変数にすぐに割り当てられる場合に、空の配列の型を決定する別の方法を許可しています。
const arr3 = [];
この方法は、初期化子なしで宣言された変数の型付けを彷彿とさせます。Flowは、変数の型を定義するために「最初」の代入または代入を選択しようとします。空の配列の場合、代入は次のいずれかです。
- インデックス付きの書き込みステートメント
a[i] = e;
、または - 配列の
push
呼び出しa.push(e)
。
どちらの場合も、e
の型が配列要素の型として使用されます。
いくつかの例を次に示します。
直線的なコード
最初の代入が見つかると、配列要素の型は代入された式の型に固定されます。異なる型の要素を持つ配列への後続の書き込みはエラーです。
1const arr3 = [];2arr3.push(42); // arr3 is inferred as Array<number>3arr3.push("abc"); // Error!
3:11-3:15: Cannot call `arr3.push` because string [1] is incompatible with number [2] in array element. [incompatible-call]
条件付きコード
配列が条件付きステートメントの兄弟ブランチで割り当てられている場合、配列要素の型は割り当てられた型のユニオンに固定されます。
1declare const cond: boolean;2
3const arr4 = [];4if (cond) {5 arr4[0] = 42;6} else {7 arr4.push("abc");8}9// arr4 is inferred as Array<number | string>10arr4.push("def"); // Works11arr4[0] = true; // Error!
11:11-11:14: Cannot assign `true` to `arr4[0]` because: [incompatible-type] Either boolean [1] is incompatible with number [2]. Or boolean [1] is incompatible with string [3].
より近いスコープが優先される
代入が発生する複数のスコープがある場合、代入の浅いスコープが優先されます。
1const arr5 = [];2function f() {3 arr5.push(42); // Error! 4}5f();6arr5.push("abc"); // This assignment wins. arr5 is inferred as Array<string>7arr5.push(1); // Error!
3:13-3:14: Cannot call `arr5.push` because number [1] is incompatible with string [2] in array element. [incompatible-call]7:11-7:11: Cannot call `arr5.push` because number [1] is incompatible with string [2] in array element. [incompatible-call]
配列アクセスは安全ではありません
配列から要素を取得するとき、常にundefined
である可能性があります。配列の範囲外のインデックスにアクセスしたか、要素が「スパース配列」であるために存在しない可能性があります。
たとえば、配列の範囲外の要素にアクセスしている可能性があります。
1const array: Array<number> = [0, 1, 2];2const value: number = array[3]; // Works3 // ^ undefined
または、「スパース配列」の場合、存在しない要素にアクセスしている可能性があります。
1const array: Array<number> = [];2
3array[0] = 0;4array[2] = 2;5
6const value: number = array[1]; // Works7 // ^ undefined
これを安全にするために、Flowはすべての配列アクセスを「場合によっては未定義」とマークする必要があります。
Flowは、これを使用するのが非常に不便になるため、これを行いません。配列にアクセスするときに、取得するすべての値の型を絞り込む必要がありました。
1const array: Array<number> = [0, 1, 2];2const value: number | void = array[1];3
4if (value !== undefined) {5 // number6}
非推奨の配列型ショートハンド構文
Array<T>
構文の代替として、T[]
があります。この構文は非推奨であり、将来的に廃止される可能性があります。
1const arr: number[] = [0, 1, 2, 3];
?Type[]
は?Array<T>
と同等であり、Array<?T>
ではないことに注意してください。
1const arr1: ?number[] = null; // Works2const arr2: ?number[] = [1, 2]; // Works3const arr3: ?number[] = [null]; // Error!
3:26-3:29: Cannot assign array literal to `arr3` because null [1] is incompatible with number [2] in array element. [incompatible-type]
Array<?T>
にしたい場合は、(?Type)[]
のように括弧を使用できます。
1const arr1: (?number)[] = null; // Error! 2const arr2: (?number)[] = [1, 2]; // Works3const arr3: (?number)[] = [null]; // Works
1:27-1:30: Cannot assign `null` to `arr1` because null [1] is incompatible with array type [2]. [incompatible-type]