Skip to main content

数组

数组类型表示长度未知的列表,其中所有项都具有相同的类型。这与 元组类型 形成对比,元组类型 具有固定长度,并且每个元素可以具有不同的类型。

¥Array types represent lists of unknown length, where all items have the same type. This is in contrast to tuple types, which have a fixed length and where each element can have a different type.

JavaScript 数组字面量值可用于创建元组和数组类型:

¥JavaScript array literal values can be used to create both tuple and array types:

1const arr: Array<number> = [1, 2, 3]; // As an array type2const tup: [number, number, number] = [1, 2, 3]; // As a tuple type

Array 类型

¥Array Type

类型 Array<T> 表示类型 T 的项目数组。例如,数字数组为 Array<number>

¥The type Array<T> represents an array of items of type T. For example, an array of numbers would be Array<number>:

1const arr: Array<number> = [1, 2, 3];

你可以在 Array<T> 中放置任何类型:

¥You can put any type within Array<T>:

1const arr1: Array<boolean> = [true, false, true];2const arr2: Array<string> = ["A", "B", "C"];3const arr3: Array<mixed> = [1, true, "three"];

$ReadOnlyArray 类型

¥$ReadOnlyArray Type

你可以使用类型 $ReadOnlyArray<T> 而不是 Array<T> 来表示无法更改的 只读 数组。你不能直接写入只读数组,也不能使用改变数组的方法,如 .push().unshift() 等。

¥You can use the type $ReadOnlyArray<T> instead of Array<T> to represent a read-only array which cannot be mutated. You can't write to a read-only array directly, and can't use methods which mutate the array like .push(), .unshift(), etc.

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!

请注意,$ReadOnlyArray<T> 类型的数组仍然可以包含可变元素:

¥Note that an array of type $ReadOnlyArray<T> can still have mutable elements:

1const readonlyArray: $ReadOnlyArray<{x: number}> = [{x: 1}];2readonlyArray[0] = {x: 42}; // Error!3readonlyArray[0].x = 42; // Works

使用 $ReadOnlyArray 而不是 Array 的主要优点是 $ReadOnlyArray 的类型参数是 协变量,而 Array 的类型参数是 不变的。这意味着 $ReadOnlyArray<number>$ReadOnlyArray<number | string> 的子类型,而 Array<number> 不是 Array<number | string> 的子类型。因此,在各种类型元素的数组的类型注释中使用 $ReadOnlyArray 通常很有用。以以下场景为例:

¥The main advantage to using $ReadOnlyArray instead of Array is that $ReadOnlyArray's type parameter is covariant while Array's type parameter is invariant. That means that $ReadOnlyArray<number> is a subtype of $ReadOnlyArray<number | string> while Array<number> is NOT a subtype of Array<number | string>. So it's often useful to use $ReadOnlyArray in type annotations for arrays of various types of elements. Take, for instance, the following scenario:

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!

由于 someOperation 函数的参数 arr 的类型为可变 Array,因此可以在该范围内将字符串推入其中,这将破坏外部 array 变量的类型约定。在这种情况下,通过将参数注释为 $ReadOnlyArray,Flow 可以确保不会发生这种情况,并且不会发生错误:

¥Since the parameter arr of the someOperation function is typed as a mutable Array, pushing a string into it would be possible inside that scope, which would then break the type contract of the outside array variable. By annotating the parameter as $ReadOnlyArray instead in this case, Flow can be sure this won't happen and no errors will occur:

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> 代表所有数组和所有 元组 的超类型:

¥$ReadOnlyArray<mixed> represents the supertype of all arrays and all tuples:

1const tup: [number, string] = [1, 'hi'];2const arr: Array<number> = [1, 2];3
4function f(xs: $ReadOnlyArray<mixed>) { /* ... */ }5
6f(tup); // Works7f(arr); // Works

空数组字面量

¥Empty Array Literals

当涉及到 注释要求 时,空数组字面量 ([]) 在 Flow 中会进行特殊处理。它们的特殊之处在于它们不包含足够的信息来确定它们的类型,同时它们太常见,以至于不需要在其直接上下文中添加类型注释。

¥Empty array literals ([]) are handled specially in Flow when it comes to their annotation requirements. What makes them special is that they do not contain enough information to determine their type, and at the same time they are too common to always require type annotations in their immediate context.

因此,输入空数组 Flow 遵循以下规则:

¥So, to type empty arrays Flow follows these rules:

上下文推断

¥Contextual Inference

首先,如果 上下文信息 存在,我们将用它来确定数组元素类型:

¥First, if contextual information exists, we'll use it to determine the array element type:

1function takesStringArray(x: Array<string>): void {}2
3const arr1: Array<string> = [];4takesStringArray([]);

在这两种情况下,[] 将被键入为 Array<string>

¥In both cases, the [] will be typed as an Array<string>.

请注意,为了使上下文信息发挥作用,类型需要在数组定义时可用。这意味着以下代码中的最后两行将出错:

¥Note that for the contextual information to work, the type needs to be available right at the definition of the array. This means that the last two lines in the following code will error:

1function takesStringArray(x: Array<string>): void {}2
3const arr2 = [];4takesStringArray(arr2);

第二个错误是由于 arr2 被推断为 Array<empty>,这导致调用 takesStringArray 时出现另一个错误。

¥The second error is due to arr2 being inferred as Array<empty> which leads to another error at the call to takesStringArray.

初始化推断

¥Initializer Inference

当立即将空数组分配给变量时,Flow 允许通过另一种方式来确定空数组的类型:

¥Flow allows another way to determine the types of empty arrays when they are immediately assigned to a variable:

const arr3 = [];

它的方式让人想起 没有初始化器的变量 的输入:Flow 尝试选择 "第一的" 赋值或对变量的赋值来定义其类型。在空数组的情况下,赋值可以是

¥The way it does this is reminiscent of the typing of variables without initializers: Flow tries to choose the "first" assignment or assignments to the variable to define its type. In the case of empty arrays, an assignment is either

  • 索引写入语句 a[i] = e;,或

    ¥an indexed write statement a[i] = e;, or

  • 数组 push 调用 a.push(e)

    ¥an array push call a.push(e).

在任何一种情况下,e 的类型都用作数组元素的类型。

¥In either case the type of e is used as the type of the array element.

这里有些例子:

¥Here are some examples:

直线代码

¥Straight-line Code

一旦找到第一个分配项,数组元素的类型就会固定到分配表达式的类型。后续写入包含不同类型元素的数组会出错:

¥Once the first assignemnt has been found, the type of the array element is pinned to that of the assigned expression. Subsequent writes to array with elements of a different type are errors:

1const arr3 = [];2arr3.push(42); // arr3 is inferred as Array<number>3arr3.push("abc"); // Error!

条件代码

¥Conditional Code

如果在条件语句的同级分支中分配数组,则数组元素的类型将固定到分配类型的并集:

¥If the array is assigned in sibling branches of conditional statements, the type of the array element is pinned to the union of the assigned types:

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!

更近的范围获胜

¥Nearer Scope Wins

当存在多个发生赋值的作用域时,首选浅赋值作用域:

¥Shallow scope of assignment is prefered when there are multiple scopes where assignments happen:

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!

数组访问不安全

¥Array access is unsafe

当你从数组中检索一个元素时,它总是有可能是 undefined。你可能访问了超出数组范围的索引,或者该元素不存在,因为它是 "稀疏数组"。

¥When you retrieve an element from an array there is always a possibility that it is undefined. You could have either accessed an index which is out of the bounds of the array, or the element could not exist because it is a "sparse array".

例如,你可能正在访问超出数组范围的元素:

¥For example, you could be accessing an element that is out of the bounds of the array:

1const array: Array<number> = [0, 1, 2];2const value: number = array[3]; // Works3                         // ^ undefined

或者你可能正在访问一个不存在的元素(如果它是 "稀疏数组"):

¥Or you could be accessing an element that does not exist if it is a "sparse array":

1const array: Array<number> = [];2
3array[0] = 0;4array[2] = 2;5
6const value: number = array[1]; // Works7                         // ^ undefined

为了确保安全,Flow 必须将每个数组访问标记为 "可能未定义"。

¥In order to make this safe, Flow would have to mark every single array access as "possibly undefined".

Flow 不会这样做,因为使用起来非常不方便。你将被迫改进访问数组时获得的每个值的类型。

¥Flow does not do this because it would be extremely inconvenient to use. You would be forced to refine the type of every value you get when accessing an array.

1const array: Array<number> = [0, 1, 2];2const value: number | void = array[1];3
4if (value !== undefined) {5  // number6}

不鼓励的数组类型简写语法

¥Discouraged Array Type Shorthand Syntax

Array<T> 语法有一个替代方案:T[]。不鼓励使用此语法,并且将来可能会弃用此语法。

¥There is an alternative to the Array<T> syntax: T[]. This syntax is discouraged and may be deprecated in the future.

1const arr: number[] = [0, 1, 2, 3];

请注意,?Type[] 相当于 ?Array<T>,而不是 Array<?T>

¥Just note that ?Type[] is the equivalent of ?Array<T> and not Array<?T>.

1const arr1: ?number[] = null;   // Works2const arr2: ?number[] = [1, 2]; // Works3const arr3: ?number[] = [null]; // Error!

如果你想将其设为 Array<?T>,你可以使用括号,例如:(?Type)[]

¥If you want to make it Array<?T> you can use parenthesis like: (?Type)[]

1const arr1: (?number)[] = null;   // Error!2const arr2: (?number)[] = [1, 2]; // Works3const arr3: (?number)[] = [null]; // Works