探索 TypeScript 元組的用例

原文作者: Alexander Nnakwue

原文地址:https://blog.logrocket.com/exploring-use-cases-typescript-tuples/

翻譯:一川

元組擴展了數組數據類型的功能。使用元組,我們可以輕鬆構造特殊類型的數組,其中元素相對於索引或位置是固定類型的。由於 TypeScript 的性質,這些元素類型在初始化時是已知的。使用元組,我們可以定義可以存儲在數組中每個位置的數據類型。

在本教程中,我們將介紹 TypeScript 中命名元組的實際用例和應用程序。我們將瞭解這種數據類型的重要性以及爲什麼在某些情況下首選它。在一天結束時,我們將看到這種數據類型如何有助於改進 TypeScript 語言,根據改進的文檔、可維護的代碼和開發人員的生產力允許更嚴格的規則。

在我們開始之前,讀者應該熟悉 TypeScript 和類型的基礎知識。要了解有關此主題的更多信息,請查看 TypeScript 文檔的這一部分。現在,讓我們開始吧。

什麼是元組?

元組就像具有額外功能的高級數組,可確保類型安全,特別是當我們需要考慮包含具有多個已知類型的固定數量的元素的列表時。

數組和元組之間的主要區別在於,當我們爲元組賦值時,這些值必須以相同的順序與元組聲明中定義的類型匹配。另一方面,數組可以支持具有any類型或按位 OR ( | ) 運算符的多種類型,但元素的順序或結構不起作用。

什麼是命名元組?

命名元組提供了一種結構化方法,用於定義具有命名屬性的數據。命名元組結合了數組和對象的優點,以清晰簡潔的方式表示數據點。此外,命名元組增強了代碼的可讀性,並通過爲屬性分配名稱來明確您的意圖。

若要在 TypeScript 中定義命名元組,請使用方括號和類型註釋的組合來指定屬性的名稱和類型。下面介紹如何在 TypeScript 中定義命名類型:

type MyNamedTuple = [name: string, age: number, isAdmin: boolean];

您已經定義了一個 MyNamedTuple 具有三個屬性的命名元組:name的類型 stringage的類型numberisAdmin的類型boolean 。類型定義中屬性的順序決定了實例化時元組中元素的順序。

定義命名元組類型後,可以通過爲屬性賦值來聲明和初始化該類型的變量,如下所示:

const person: MyNamedTuple = ['John Doe', 30, false];

您聲明瞭該 MyNamedTuple 類型的變量 person 併爲其分配了值。值的順序對應於命名元組中定義的屬性順序。

使用元組的好處

在 TypeScript 程序中使用元組有很多好處。首先,元組是固定長度的序列,允許您定義元素的有序集合。當您需要表示一系列值(如座標 ( x , y ) 或 RGB 顏色值 ( red , green , blue ) 時,元組很方便。固定長度有助於確保元組中具有正確數量的元素。

此外,您可以輕鬆地解構元組以提取單個元素,從而可以方便地使用一行代碼將每個元素分配給單獨的變量。解構元組可以提高可讀性,尤其是在使用返回多個值的函數時。

此外,元組與數組有一些相似之處;您可以對它們執行類似數組的操作。您可以按索引訪問單個元素,使用循環迭代它們並使用 mapfilterreduce 。但是,與數組不同,元組具有固定長度,這可確保元組的結構保持不變。下面是一個示例:

// Declare a tuple type
type MyTuple = [number, string, boolean];

// Create a tuple
const myTuple: MyTuple = [10, "Hello", true];

// Iterate over tuple elements with a loop
for (const element of myTuple) {
  console.log(element);
}

// Use methods like map, filter, and reduce
const mappedTuple: MyTuple = myTuple.map((element) => element * 2);
console.log(mappedTuple); // Output: [20, "HelloHello", NaN]

const filteredTuple: MyTuple = myTuple.filter((element) => typeof element === "string");
console.log(filteredTuple); // Output: [NaN, "Hello", NaN]

const reducedValue: number = myTuple.reduce((acc, curr) => acc + (typeof curr === "number" ? curr : 0), 0);
console.log(reducedValue); // Output: 10

以下是在元組上運行常用數組操作的結果:

由於元組的優點和功能,元組優先於數組。元組強制實施固定長度,提供類型安全性,並允許異構數據。TypeScript 支持元組上的結構模式匹配,並啓用簡潔的函數簽名。

解構賦值、只讀屬性和內存效率是額外的好處。類型推理和命名元組元素使元組對於結構化數據非常強大。

數組和元組數據類型簡介

在我們開始探索 TypeScript 中元組的用例之前,讓我們簡要探討一些可以使用數組的簡單案例,以及元組如何在同一場景中完美地適應甚至更好。

在 TypeScript 中,我們可以聲明一個特定數據類型的數組。例如,我們可以通過指定該元素的類型後跟方括號來聲明一個數字數組:[]。讓我們看看如何做到這一點:

let arr: number[];

arr = [1, 2, 3];

正如我們從上面的例子中看到的,爲了確保類型安全(這允許更容易地註釋和記錄我們的代碼),我們需要使用數組,這允許像這樣的情況,我們有特定數據類型的列表。事實上,這就是像 TypeScript 這樣的類型語言的本質。

具有多種數據類型的數組

對於具有多種數據類型的數組,我們可以使用 any 類型或 | (按位 OR)運算符。但是,在這種情況下,數據的順序不是一成不變的。讓我們看下面的一個例子:

let arr: (string | number)[];
arr = ['Alex', 2020];
console.log(arr);

從上面的例子中,我們可以決定在字符串之前傳遞數字,它仍然有效。在這種情況下,實例化數組時傳遞數據的順序無關緊要,因爲我們具有指定類型的組合。這正是元組想要解決的問題。

使用元組,我們可以擁有一個多種數據類型的列表,其中我們傳遞數據類型的順序必須符合聲明元組時的順序。本質上,元組的結構需要保持不變。讓我們看一個例子來更好地理解這個概念:

let tup: [string, number];

tup = ['Alex', 19087]

在上面的例子中,我們可以看到我們已經聲明瞭一個具有兩種基本數據類型的元組:stringnumber 。請注意,當我們調用變量tup時,我們還必須按照聲明的順序傳遞元素類型。本質上,我們不能在索引爲 0 處有一個數字和索引爲 1 處有一個字符串,就像這樣:

tup = [19087, 'Alex]

如果我們這樣做了,我們將得到如下所示的錯誤:

TSError: ⨯ Unable to compile TypeScript:
index.ts:6:8 - error TS2322: Type 'number' is not assignable to type 'string'.

6 tup = [19087, 'Alex']
         ~~~~~
index.ts:6:15 - error TS2322: Type 'string' is not assignable to type 'number'.

6 tup = [19087, 'Alex']

正如我們從上面前面的例子中看到的,我們正在聲明一個數字數組並用值初始化它。只要我們只處理數字的元素類型,這就可以工作。爲了考慮具有多種數據類型的數組,我們可以使用 any 類型或運算符,儘管在這種情況下,不能保證數據的順序或 | 結構,這可能不是我們想要的。

但是,使用元組,我們可以確保數據類型和要傳遞的數據順序的嚴格性。元組允許在具有固定數量的元素的元素類型周圍指定已知類型邊界。

TypeScript 元組用例

由於元組允許我們在數組中定義固定類型和順序,因此在處理以順序方式相互關聯的數據(其中順序很重要)時,它們是最好的選擇。這樣,我們可以輕鬆地以預定的方式訪問元素,從而使我們期望的響應在行爲上可預測。

下面,我們將基於 v4.2 版本在 TypeScript 中探索元組類型的更多用例,這些用例通常圍繞在函數簽名中提取和傳播參數列表。

在 REST 參數中使用元組

REST 參數語法將參數收集到單個數組變量中,然後展開它們。在最近的 TypeScript 版本中,我們現在可以使用元組類型將 REST 參數擴展爲離散參數。這意味着,當類型 tuple 用作REST參數時,它會平展到參數列表的其餘部分。

簡單來說,當 REST 參數是元組類型時,元組類型可以擴展爲一系列參數列表。

請考慮以下示例:

declare function example(...args: [string, number]): void;

REST 參數將元組類型的元素擴展爲離散參數。調用函數時, args 表示爲 REST 參數的函數將展開爲與下面的函數簽名完全相同:

declare function example(args0: string, args1: number): void;

因此,REST 參數語法收集溢出到數組或元組中的參數。總之,元組類型迫使我們將適當的類型傳遞給相應的函數簽名。TypeScript v4.2 增加了在前導或中間元素上展開的功能。這很方便,因爲您可以使用 REST 參數在前導或中間參數上創建可變參數函數,如下所示:

type Matches = [string, boolean];

const arsenal: Matches = ['Man City', true];
const city: Matches = ['Man United', true];
const hotspur: Matches = ['Liverpool', true];

function processMatches(...matches: [...Matches[], string]): void {
  const lastMatch = matches.pop();
  console.log('Previous matches:');
  for (const match of matches) {
    console.log(match[0]);
  }
  console.log('Last match:', lastMatch);
}

processMatches(arsenal, city, hotspur, 'Chelsea vs. Arsenal');

processMatches 函數接受具有展開語法的 ... 可變參數。該參數是 ,[...Matches[], string]這意味着它需要兩個或多個類型的 Matches 元組,後跟一個字符串。

使用元組展開表達式

擴展語法將數組或對象的元素擴展爲其元素。擴展運算符還可以擴展元組的元素。當函數調用包含元組類型的擴展表達式作爲參數時,擴展表達式將擴展爲與元組類型的元素對應的參數序列。讓我們看下面的一個例子:

type Value = [number, number];

const sample = (...value: Value) ={
  // do  something with value here
};

// create a type
let sampleTuple: Value;

sampleTuple = [20, 40];

// Passing the values as literals:
sample(20, 40);

// Passing indexes to the corresponding sampleTuple tuple
sample(sampleTuple[0], sampleTuple[1]);

// Using the spread operator to pass the full sampleTuple tuple
sample(...sampleTuple);

注意,從上面的例子中我們可以看到,我們已經聲明瞭一個元組類型,並將其作爲參數傳遞給函數簽名。

解構值

因爲元組是底層的數組,我們可以像解構數組一樣解構它們。重要的是要注意,解構變量獲取相應元組元素的類型。讓我們看一個例子:

let tuple: [number, string, boolean];

tuple = [7, "hello", true];

let [a, b, c] = tuple; 

// a: number, b: string, c: boolean

TypeScript 元組最佳實踐

雖然元組有其優點,但在使用元組之前必須考慮權衡。元組不如數組和對象靈活,修改或擴展元組可能很麻煩。如果數據結構需要頻繁修改或其他屬性,您可能會發現數組或對象更合適。

創建有意義且可重用的元組類型的提示

創建定義明確且可重用的元組類型對於保持清晰度和減少代碼重複至關重要。讓我們討論在 TypeScript 中定義和使用元組類型時要考慮的一些技巧。首先,請確保爲元組中的元素分配有意義的名稱,以提高可讀性並幫助其他人瞭解每個值的用途。例如,請考慮 [x, y]`` [latitude, longitude]

此外,TypeScript 的類型推斷系統可以根據元組類型的分配值自動推斷元組類型。不應顯式定義類型,而應依靠類型推斷來減少冗餘並提高代碼可維護性。如果元組中的某些元素是可選的,請使用聯合類型來指示可能存在的元素。靈活性可確保元組類型適應多種方案。

當元組很複雜或跨代碼庫的多個部分重用時,請考慮將它們抽象爲接口或類型別名以實現可重用性,提高代碼可讀性,並允許將來更易於訪問的修改和擴展。通過遵循這些提示,您可以創建有意義且可重用的元組類型,以增強 TypeScript 程序的清晰度和可維護性。

使用元組時要避免的錯誤

開發人員應注意一些常見的陷阱,以避免潛在的問題。在本節中,我們將介紹使用元組時要避免的一些常見錯誤。默認情況下,元組是不可變的。嘗試修改元組的值將導致編譯錯誤。避免直接更改元組元素;創建具有所需修改的新元組。

請記住,元組依賴於其元素的順序來維護其結構。意外地對元素重新排序可能會引入難以發現的錯誤。爲了防止這種情況,請使用清晰的描述性變量名稱,並使用解構或命名元組元素按名稱訪問值,而不是僅依賴它們的順序。

最後,過度使用元組會使代碼更難理解和維護。如果數據結構需要頻繁修改,請考慮使用對象或數組。避免這些錯誤將幫助您有效地利用 TypeScript 元組的強大功能並減少潛在的代碼錯誤。

總結

TypeScript 元組就像具有固定數量的元素的數組。它們爲我們提供了一個固定大小的容器,可以存儲多種類型的值,其中順序和結構非常重要。當我們確切地知道數組中允許多少種類型時,最好使用此數據類型。衆所周知,在原始定義的長度之外分配索引將導致 TypeScript 編譯器出錯。

請注意,雖然可以通過元組元素的索引修改元組元素的值,但我們必須確保與聲明元組變量時提供的類型匹配。這是因爲一旦聲明,我們就無法更改元組中元素的類型甚至大小。

通過我們在這篇文章中強調的功能,可以設計強類型的高階函數,這些函數可以轉換函數及其參數列表,並且本質上確保一個健壯的、有據可查的、可維護的代碼庫,這是我們使用 TypeScript 的核心。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Pp43fO1CV_jA6mfEDLhRyQ