跳至主要內容

元組

元組類型表示固定長度清單,其中元素可以有不同類型。這與陣列類型相反,陣列類型具有未知長度,且所有元素具有相同類型。

元組基礎

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]

您也可以使用PartialRequired工具類型分別使所有元素成為選擇性或必要

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]

採用

若要使用標籤元組元素(包括元素上的選擇性元素和變異註解)和元組展開元素,您需要升級您的基礎架構,使其支援語法