元组
元组类型表示一个固定长度的列表,其中元素可以具有不同的类型。这与数组类型形成对比,数组类型具有未知长度,并且所有元素都具有相同的类型。
元组基础
JavaScript 数组字面量值可用于创建元组和数组类型
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]
语法创建元组类型
1const tuple1: [number] = [1];2const tuple2: [number, boolean] = [1, true];3const tuple3: [number, boolean, string] = [1, true, "three"];
当您从元组中获取特定索引处的某个值时,它将返回该索引处的类型
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!
尝试访问不存在的索引会导致索引超出范围错误
1const tuple: [number, boolean, string] = [1, true, "three"];2
3const none = tuple[3]; // Error!
3:14-3:21: Cannot get `tuple[3]` because tuple type [1] only has 3 elements, so index 3 is out of bounds. [invalid-tuple-index]
如果 Flow 不知道您尝试访问哪个索引,它将返回所有可能的类型
1const tuple: [number, boolean, string] = [1, true, "three"];2
3function getItem(n: number) {4 const val: number | boolean | string = tuple[n];5 // ...6}
在元组中设置新值时,新值必须与该索引处的类型匹配
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!
7:12-7:16: Cannot assign `"bar"` to `tuple[0]` because string [1] is incompatible with number [2]. [incompatible-type]8:12-8:13: Cannot assign `42` to `tuple[1]` because number [1] is incompatible with boolean [2]. [incompatible-type]9:12-9:16: Cannot assign `false` to `tuple[2]` because boolean [1] is incompatible with string [2]. [incompatible-type]
严格执行元组长度(元数)
元组的长度称为“元数”。Flow 严格执行元组的长度。
这意味着较短的元组不能用在较长元组的位置
1const tuple1: [number, boolean] = [1, true];2
3const tuple2: [number, boolean, void] = tuple1; // Error!
3:41-3:46: Cannot assign `tuple1` to `tuple2` because tuple type [1] has 2 elements but tuple type [2] has 3 elements. [invalid-tuple-arity]
同样,较长的元组也不能用在较短元组的位置
1const tuple1: [number, boolean, void] = [1, true, undefined];2
3const tuple2: [number, boolean] = tuple1; // Error!
3:35-3:40: Cannot assign `tuple1` to `tuple2` because tuple type [1] has 3 elements but tuple type [2] has 2 elements. [invalid-tuple-arity]
可选元素 将元数变为一个范围。
元组与数组类型不匹配
由于 Flow 不知道数组的长度,因此 Array<T>
类型不能传递给元组
1const array: Array<number> = [1, 2];2
3const tuple: [number, number] = array; // Error!
3:33-3:37: Cannot assign `array` to `tuple` because array type [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]
同样,元组类型也不能传递给 Array<T>
类型,因为这样您就可以以不安全的方式修改元组(例如,向其中push
第三个元素)
1const tuple: [number, number] = [1, 2];2
3const array: Array<number> = tuple; // Error!
3:30-3:34: Cannot assign `tuple` to `array` because tuple type [1] is incompatible with array type [2]. [incompatible-type]
但是,您可以将其传递给$ReadOnlyArray
类型,因为不允许修改
1const tuple: [number, number] = [1, 2];2
3const array: $ReadOnlyArray<number> = tuple; // Works!
不能对元组使用修改数组的方法
您不能使用修改元组的 Array.prototype
方法,只能使用不修改元组的方法
1const tuple: [number, number] = [1, 2];2tuple.join(', '); // Works!3
4tuple.push(3); // Error!
4:7-4:10: Cannot call `tuple.push` because property `push` is missing in `$ReadOnlyArray` [1]. [prop-missing]
长度细化
您可以通过长度来细化联合 元组
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}
元组元素标签
注意:本节和后续部分要求您的工具更新,如本页末尾的“采用”部分所述。
您可以向元组元素添加标签。此标签不会影响元组元素的类型,但在自记录元组元素的用途时很有用,尤其是在多个元素具有相同类型时。
1type Range = [x: number, y: number];
标签对于向元素添加方差注解或可选性修饰符也是必需的(因为如果没有标签,我们将遇到解析歧义)。
方差注解和只读元组
您可以向带标签的元组元素添加方差 注解(表示只读/只写),就像对对象属性一样
1type T = [+foo: number, -bar: string];
这允许您将元素标记为只读或只写。例如
1function f(readOnlyTuple: [+foo: number, +bar: string]) {2 const n: number = readOnlyTuple[0]; // OK to read3 readOnlyTuple[1] = 1; // ERROR! Cannot write 4}
3:3-3:18: Cannot assign `1` to `readOnlyTuple[1]` because tuple element at index `1` [1] labeled `bar` is not writable. [cannot-write]3:22-3:22: Cannot assign `1` to `readOnlyTuple[1]` because number [1] is incompatible with string [2]. [incompatible-type]
您也可以使用$ReadOnly
在元组类型上作为将每个属性标记为只读的简写
1type T = $ReadOnly<[number, string]>; // Same as `[+a: number, +b: string]`
可选元组元素
您可以在元素标签后使用 ?
将元组元素标记为可选。这允许您省略可选元素。可选元素必须位于元组类型的末尾,在所有必需元素之后。
1type T = [foo: number, bar?: string];2[1, "s"] as T; // OK: has all elements3[1] as T; // OK: skipping optional element
您不能将 undefined
写入可选元素 - 如果要这样做,请在元素类型中添加 | void
1type T = [foo?: number, bar?: number | void];2declare const x: T;3x[0] = undefined; // ERROR 4[undefined] as T; // ERROR 5
6x[1] = undefined; // OK: we've added `| void` to the element type
3:8-3:16: Cannot assign `undefined` to `x[0]` because you cannot assign undefined [1] to optional tuple element [2] (to do so, add `| void` to the tuple element type). [incompatible-type]4:2-4:10: Cannot cast array literal to `T` because you cannot assign undefined [1] to optional tuple element [2] (to do so, add `| void` to the tuple element type) in index 0. [incompatible-cast]
您也可以使用Partial
和Required
实用类型分别使所有元素可选或必需
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
5:1-5:2: Cannot cast array literal to required of `AllOptional` because empty array literal [1] has 0 elements but `AllOptional` [2] has 2 elements. [invalid-tuple-arity]
包含可选元素的元组的元数(长度)是一个范围,而不是单个数字。例如,[number, b?: string]
的长度为 1-2。
元组展开
您可以将元组类型展开到另一个元组类型中,以创建一个更长的元组类型
1type A = [number, string];2type T = [...A, boolean]; // Same as `[number, string, boolean]`3[1, "s", true] as T; // OK
元组展开保留标签、方差和可选性。您不能将数组展开到元组中,只能展开其他元组。
在值级别,如果您将包含可选元素的元组展开到数组字面量中,那么您不能在该展开之后有任何内容并保留数组值的元组视图。这是因为包含可选元素的元组的长度是一个范围,因此我们不知道任何后续值将在哪个索引处。您仍然可以将此值类型化为适当的 Array<T>
类型 - 只有值的元组视图会受到影响。
1const a: [foo?: 1] = [];2const b = [0, ...a, 2]; // At runtime this is `[0, 2]`3b as [0, 1 | void, 2]; // ERROR 4b 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]; // ERROR 10e as Array<number | void>; // OK
3:1-3:1: Cannot cast `b` to tuple type because array literal [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]9:1-9:1: Cannot cast `e` to tuple type because array literal [1] has an unknown number of elements, so is incompatible with tuple type [2]. [invalid-tuple-arity]
采用
要使用带标签的元组元素(包括可选元素和元素上的方差注解)和元组展开元素,您需要升级您的基础设施,使其支持该语法
flow
和flow-parser
:0.212.0prettier
: 3babel
使用babel-plugin-syntax-hermes-parser
。有关设置说明,请参阅我们的 Babel 指南。eslint
使用hermes-eslint
。有关设置说明,请参阅我们的 ESLint 指南。