Skip to main content

元组

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

¥Tuple types represent a fixed length list, where the elements can have different types. This is in contrast to array types, which have an unknown length and all elements have the same type.

元组基础知识

¥Tuple Basics

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

在 Flow 中,你可以使用 [type1, type2, type3] 语法创建元组类型:

¥In Flow you can create tuple types using the [type1, type2, type3] syntax:

1const tuple1: [number] = [1];2const tuple2: [number, boolean] = [1, true];3const tuple3: [number, boolean, string] = [1, true, "three"];

当你从特定索引处的元组获取值时,它将返回该索引处的类型:

¥When you get a value from a tuple at a specific index, it will return the type at that index:

1const tuple: [number, boolean, string] = [1, true, "three"];2
3const num: number = tuple[0]; // Works!4const bool: boolean = tuple[1]; // Works!5const str: string  = tuple[2]; // Works!

尝试访问不存在的索引会导致索引越界错误:

¥Trying to access an index that does not exist results in an index-out-of-bounds error:

1const tuple: [number, boolean, string] = [1, true, "three"];2
3const none = tuple[3]; // Error!

如果 Flow 不知道你正在尝试访问哪个索引,它将返回所有可能的类型:

¥If Flow doesn't know which index you are trying to access it will return all possible types:

1const tuple: [number, boolean, string] = [1, true, "three"];2
3function getItem(n: number) {4  const val: number | boolean | string = tuple[n];5  // ...6}

在元组内设置新值时,新值必须与该索引处的类型匹配:

¥When setting a new value inside a tuple, the new value must match the type at that index:

1const tuple: [number, boolean, string] = [1, true, "three"];2
3tuple[0] = 2;     // Works!4tuple[1] = false; // Works!5tuple[2] = "foo"; // Works!6
7tuple[0] = "bar"; // Error!8tuple[1] = 42;    // Error!9tuple[2] = false; // Error!

严格执行元组长度(元数)

¥Strictly enforced tuple length (arity)

元组的长度称为 "数量"。Flow 中严格执行元组的长度。

¥The length of the tuple is known as the "arity". The length of a tuple is strictly enforced in Flow.

这意味着不能使用较短的元组来代替较长的元组:

¥This means that a shorter tuple can't be used in place of a longer one:

1const tuple1: [number, boolean] = [1, true];2
3const tuple2: [number, boolean, void] = tuple1; // Error!

此外,不能使用较长的元组来代替较短的元组:

¥Also, a longer tuple can't be used in place of a shorter one:

1const tuple1: [number, boolean, void] = [1, true, undefined];2
3const tuple2: [number, boolean] = tuple1; // Error!

可选元素 使数量成为一个范围。

¥Optional elements make the arity into a range.

元组与数组类型不匹配

¥Tuples don't match array types

由于 Flow 不知道数组的长度,因此无法将 Array<T> 类型传递到元组中:

¥Since Flow does not know the length of an array, an Array<T> type cannot be passed into a tuple:

1const array: Array<number> = [1, 2];2
3const tuple: [number, number] = array; // Error!

此外,元组类型不能传递给 Array<T> 类型,因为那时你可以以不安全的方式改变元组(例如,将第三个项目 pushing 到它上面):

¥Also a tuple type cannot be passed into to an Array<T> type, since then you could mutate the tuple in an unsafe way (for example, pushing a third item onto it):

1const tuple: [number, number] = [1, 2];2
3const array: Array<number> = tuple; // Error!

但是,你可以将其传递给 $ReadOnlyArray 类型,因为不允许突变:

¥However, you can pass it to a $ReadOnlyArray type, since mutation is disallowed:

1const tuple: [number, number] = [1, 2];2
3const array: $ReadOnlyArray<number> = tuple; // Works!

无法对元组使用修改数组方法

¥Cannot use mutating array methods on tuples

你不能使用 Array.prototype 方法来改变元组,只能使用不改变元组的方法:

¥You cannot use Array.prototype methods that mutate the tuple, only ones that do not:

1const tuple: [number, number] = [1, 2];2tuple.join(', '); // Works!3
4tuple.push(3); // Error!

长度细化

¥Length refinement

你可以根据元组的长度来优化 联合 个元组:

¥You can refine a union of tuples by their length:

1type Union = [number, string] | [boolean];2function f(x: Union) {3  if (x.length === 2) {4    // `x` is `[number, string]`5    const n: number = x[0]; // OK6    const s: string = x[1]; // OK7  } else {8    // `x` is `[boolean]`9    const b: boolean = x[0];10  }11}

元组元素标签

¥Tuple element labels

注意:本节和以下部分要求你按照本页末尾的 "采纳" 部分所述更新工具。

¥NOTE: This and the following sections require your tooling to be updated as described in the "Adoption" section at the end of this page.

你可以向元组元素添加标签。此标签不会影响元组元素的类型,但对于自我记录元组元素的用途很有用,特别是当多个元素具有相同类型时。

¥You can add a label to tuple elements. This label does not affect the type of the tuple element, but is useful in self-documenting the purpose of the tuple elements, especially when multiple elements have the same type.

1type Range = [x: number, y: number];

标签对于向元素添加方差注释或可选修饰符也是必要的(因为如果没有标签,我们就会有解析歧义)。

¥The label is also necessary to add a variance annotation or optionality modifier to an element (as without the label we would have parsing ambiguities).

方差注释和只读元组

¥Variance annotations and read-only tuples

你可以在带标签的元组元素上添加 方差 注释(表示只读/只写),就像在对象属性上一样:

¥You can add variance annotations (to denote read-only/write-only) on labeled tuple elements, just like on object properties:

1type T = [+foo: number, -bar: string];

这允许你将元素标记为只读或只写。例如:

¥This allows you to mark elements as read-only or write-only. For example:

1function f(readOnlyTuple: [+foo: number, +bar: string]) {2  const n: number = readOnlyTuple[0]; // OK to read3  readOnlyTuple[1] = 1; // ERROR! Cannot write4}

你还可以在元组类型上使用 $ReadOnly 作为将每个属性标记为只读的简写:

¥You can also use the $ReadOnly on tuple types as a shorthand for marking each property as read-only:

1type T = $ReadOnly<[number, string]>; // Same as `[+a: number, +b: string]`

可选元组元素

¥Optional tuple elements

你可以在元素标签后使用 ? 将元组元素标记为可选。这允许你省略可选元素。可选元素必须位于元组类型的末尾,位于所有必需元素之后。

¥You can mark tuple elements as optional with ? after an element’s label. This allows you to omit the optional elements. Optional elements must be at the end of the tuple type, after all required elements.

1type T = [foo: number, bar?: string];2[1, "s"] as T; // OK: has all elements3[1] as T; // OK: skipping optional element

你不能将 undefined 写入可选元素 - 如果你愿意,请将 | void 添加到元素类型:

¥You cannot write undefined to the optional element - add | void to the element type if you want to do so:

1type T = [foo?: number, bar?: number | void];2declare const x: T;3x[0] = undefined; // ERROR4[undefined] as T; // ERROR5
6x[1] = undefined; // OK: we've added `| void` to the element type

你还可以使用 PartialRequired 工具类型分别使所有元素成为可选或必需:

¥You can also use the Partial and Required utility types to make all elements optional or required respectively:

1type AllRequired = [number, string];2[] as Partial<AllRequired>; // OK: like `[a?: number, b?: string]` now3
4type AllOptional = [a?: number, b?: string];5[] as Required<AllOptional>; // ERROR: like `[a: number, b: string]` now

具有可选元素的元组的数量(长度)是一个范围而不是单个数字。例如,[number, b?: string] 的长度为 1-2。

¥Tuples with optional elements have an arity (length) that is a range rather than a single number. For example, [number, b?: string] has an length of 1-2.

元组传播

¥Tuple spread

你可以将一个元组类型扩展到另一个元组类型中以形成更长的元组类型:

¥You can spread a tuple type into another tuple type to make a longer tuple type:

1type A = [number, string];2type T = [...A, boolean]; // Same as `[number, string, boolean]`3[1, "s", true] as T; // OK

元组展开保留了标签、方差和可选性。你不能将数组扩展为元组,只能扩展为其他元组。

¥Tuple spreads preserve labels, variance, and optionality. You cannot spread arrays into tuples, only other tuples.

在值级别,如果将带有可选元素的元组展开到数组字面量中,那么在展开之后就不能再有任何内容,并保留数组值的元组视图。这是因为具有可选元素的元组的长度是一个范围,因此我们不知道任何后续值将位于哪个索引处。你仍然可以将此值键入为适当的 Array<T> 类型 - 仅值的元组视图受到影响。

¥At the value level, if you spread a tuple with optional elements into an array literal, then you cannot have anything after that spread and retain the tuple view of the array value. That is because a tuple with optional elements has a length that's a range, so we don't know at what index any subsequent values would be at. You can still type this value as the appropriate Array<T> type - only the tuple view of the value is affected.

1const a: [foo?: 1] = [];2const b = [0, ...a, 2]; // At runtime this is `[0, 2]`3b as [0, 1 | void, 2]; // ERROR4b as Array<number | void>; // OK5
6const c: [0, foo?: 1] = [0];7const d: [bar?: 2] = [2];8const e = [...c, ...d]; // At runtime this is `[0, 2]`9e as [0, foo?: 1, bar?: 2]; // ERROR10e as Array<number | void>; // OK

不精确元组

¥Inexact tuples

不精确元组类型的工作方式与 不精确的对象 类似:它们允许元组末尾有未知成员。

¥Inexact tuple types work like inexact objects: they allow for unknown members at the end of the tuple.

1[] as [...]; // OK2[1] as [...]; // OK3[1] as [number, ...]; // OK

所有元组都是不精确空元组 [...] 的子类型。

¥All tuples are subtypes of the inexact empty tuple [...].

如果展开不精确元组,结果也是不精确的。你无法在非精确元组的展开后定义元素,因为我们不知道它们应该在哪个索引处。

¥If you spread an inexact tuple, the result is also inexact. You cannot define elements after the spread of an inexact tuple, since we wouldn't know at what index they should be.

1declare const x: [1, ...];2const y = [0, ...x];3y as [0, 1]; // ERROR: it's inexact4y as [0, 1, ...]; // OK5
6[...x, 2]; // ERROR: can't have element after inexact spread

不精确元组允许你要求泛型是元组,例如

¥Inexact tuples allow you to require that a generic is a tuple, e.g.

1function mapTupleArray<T: [...], R>(2  tuples: Array<T>, // An array of tuples3  f: (...T) => R, // Function args match the tuple's types4): Array<R> {5  return tuples.map(args => f(...args));6}7mapTupleArray(8  [[1, 'hi'], [3, 'bye']],9  (x: number, y: string) => y.length === x,10); // OK11
12declare const arrays: Array<Array<number>>;13mapTupleArray(arrays, (x: number, y: number) => x + y); // ERROR: array is not a tuple

采纳

¥Adoption

要使用带标签的元组元素(包括可选元素和元素上的方差注释)和元组扩展元素,你需要升级你的基础设施以使其支持语法:

¥To use labeled tuple elements (including optional elements and variance annotations on elements) and tuple spread elements, you need to upgrade your infrastructure so that it supports the syntax:

  • flowflow-parser:0.212

    ¥flow and flow-parser: 0.212

  • prettier:3

  • babelbabel-plugin-syntax-hermes-parser (v0.15)。请参阅 我们的 Babel 指南 了解设置说明。

    ¥babel with babel-plugin-syntax-hermes-parser (v0.15). See our Babel guide for setup instructions.

  • eslinthermes-eslint (v0.15)。请参阅 我们的 ESLint 指南 了解设置说明。

    ¥eslint with hermes-eslint (v0.15). See our ESLint guide for setup instructions.

要使用不精确元组,请升级到:

¥To use inexact tuples, upgrade to:

  • flowflow-parser:0.243

    ¥flow and flow-parser: 0.243

  • prettier:3.3

  • babelbabel-plugin-syntax-hermes-parser (v0.23)。请参阅 我们的 Babel 指南 了解设置说明。

    ¥babel with babel-plugin-syntax-hermes-parser (v0.23). See our Babel guide for setup instructions.

  • eslinthermes-eslint (v0.23)。请参阅 我们的 ESLint 指南 了解设置说明。

    ¥eslint with hermes-eslint (v0.23). See our ESLint guide for setup instructions.