探索 TypeScript 元組的用例
原文作者: Alexander Nnakwue
原文地址:https://blog.logrocket.com/exploring-use-cases-typescript-tuples/
翻譯:一川
元組擴(kuò)展了數(shù)組數(shù)據(jù)類型的功能。使用元組,我們可以輕松構(gòu)造特殊類型的數(shù)組,其中元素相對于索引或位置是固定類型的。由于 TypeScript 的性質(zhì),這些元素類型在初始化時(shí)是已知的。使用元組,我們可以定義可以存儲(chǔ)在數(shù)組中每個(gè)位置的數(shù)據(jù)類型。
在本教程中,我們將介紹 TypeScript 中命名元組的實(shí)際用例和應(yīng)用程序。我們將了解這種數(shù)據(jù)類型的重要性以及為什么在某些情況下首選它。在一天結(jié)束時(shí),我們將看到這種數(shù)據(jù)類型如何有助于改進(jìn) TypeScript 語言,根據(jù)改進(jìn)的文檔、可維護(hù)的代碼和開發(fā)人員的生產(chǎn)力允許更嚴(yán)格的規(guī)則。
在我們開始之前,讀者應(yīng)該熟悉TypeScript和類型的基礎(chǔ)知識(shí)。要了解有關(guān)此主題的更多信息,請查看 TypeScript 文檔的這一部分?,F(xiàn)在,讓我們開始吧。
什么是元組?
元組就像具有額外功能的高級數(shù)組,可確保類型安全,特別是當(dāng)我們需要考慮包含具有多個(gè)已知類型的固定數(shù)量的元素的列表時(shí)。
數(shù)組和元組之間的主要區(qū)別在于,當(dāng)我們?yōu)樵M賦值時(shí),這些值必須以相同的順序與元組聲明中定義的類型匹配。另一方面,數(shù)組可以支持具有any類型或按位 OR ( | ) 運(yùn)算符的多種類型,但元素的順序或結(jié)構(gòu)不起作用。
什么是命名元組?
命名元組提供了一種結(jié)構(gòu)化方法,用于定義具有命名屬性的數(shù)據(jù)。命名元組結(jié)合了數(shù)組和對象的優(yōu)點(diǎn),以清晰簡潔的方式表示數(shù)據(jù)點(diǎn)。此外,命名元組增強(qiáng)了代碼的可讀性,并通過為屬性分配名稱來明確您的意圖。
若要在 TypeScript 中定義命名元組,請使用方括號和類型注釋的組合來指定屬性的名稱和類型。下面介紹如何在 TypeScript 中定義命名類型:
type MyNamedTuple = [name: string, age: number, isAdmin: boolean];
您已經(jīng)定義了一個(gè) MyNamedTuple 具有三個(gè)屬性的命名元組:name的類型 string,age的類型number、 isAdmin的類型boolean 。類型定義中屬性的順序決定了實(shí)例化時(shí)元組中元素的順序。
定義命名元組類型后,可以通過為屬性賦值來聲明和初始化該類型的變量,如下所示:
const person: MyNamedTuple = ['John Doe', 30, false];
您聲明了該 MyNamedTuple 類型的變量 person 并為其分配了值。值的順序?qū)?yīng)于命名元組中定義的屬性順序。
使用元組的好處
在 TypeScript 程序中使用元組有很多好處。首先,元組是固定長度的序列,允許您定義元素的有序集合。當(dāng)您需要表示一系列值(如坐標(biāo) ( x , y ) 或 RGB 顏色值 ( red , green , blue ) 時(shí),元組很方便。固定長度有助于確保元組中具有正確數(shù)量的元素。
此外,您可以輕松地解構(gòu)元組以提取單個(gè)元素,從而可以方便地使用一行代碼將每個(gè)元素分配給單獨(dú)的變量。解構(gòu)元組可以提高可讀性,尤其是在使用返回多個(gè)值的函數(shù)時(shí)。
此外,元組與數(shù)組有一些相似之處;您可以對它們執(zhí)行類似數(shù)組的操作。您可以按索引訪問單個(gè)元素,使用循環(huán)迭代它們并使用 map 、 filter 和 reduce 。但是,與數(shù)組不同,元組具有固定長度,這可確保元組的結(jié)構(gòu)保持不變。下面是一個(gè)示例:
// 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
以下是在元組上運(yùn)行常用數(shù)組操作的結(jié)果:
圖片
由于元組的優(yōu)點(diǎn)和功能,元組優(yōu)先于數(shù)組。元組強(qiáng)制實(shí)施固定長度,提供類型安全性,并允許異構(gòu)數(shù)據(jù)。TypeScript 支持元組上的結(jié)構(gòu)模式匹配,并啟用簡潔的函數(shù)簽名。
解構(gòu)賦值、只讀屬性和內(nèi)存效率是額外的好處。類型推理和命名元組元素使元組對于結(jié)構(gòu)化數(shù)據(jù)非常強(qiáng)大。
數(shù)組和元組數(shù)據(jù)類型簡介
在我們開始探索 TypeScript 中元組的用例之前,讓我們簡要探討一些可以使用數(shù)組的簡單案例,以及元組如何在同一場景中完美地適應(yīng)甚至更好。
在 TypeScript 中,我們可以聲明一個(gè)特定數(shù)據(jù)類型的數(shù)組。例如,我們可以通過指定該元素的類型后跟方括號來聲明一個(gè)數(shù)字?jǐn)?shù)組:[]。讓我們看看如何做到這一點(diǎn):
let arr: number[];
arr = [1, 2, 3];
正如我們從上面的例子中看到的,為了確保類型安全(這允許更容易地注釋和記錄我們的代碼),我們需要使用數(shù)組,這允許像這樣的情況,我們有特定數(shù)據(jù)類型的列表。事實(shí)上,這就是像TypeScript這樣的類型語言的本質(zhì)。
具有多種數(shù)據(jù)類型的數(shù)組
對于具有多種數(shù)據(jù)類型的數(shù)組,我們可以使用 any 類型或 | (按位 OR)運(yùn)算符。但是,在這種情況下,數(shù)據(jù)的順序不是一成不變的。讓我們看下面的一個(gè)例子:
let arr: (string | number)[];
arr = ['Alex', 2020];
console.log(arr);
從上面的例子中,我們可以決定在字符串之前傳遞數(shù)字,它仍然有效。在這種情況下,實(shí)例化數(shù)組時(shí)傳遞數(shù)據(jù)的順序無關(guān)緊要,因?yàn)槲覀兙哂兄付愋偷慕M合。這正是元組想要解決的問題。
使用元組,我們可以擁有一個(gè)多種數(shù)據(jù)類型的列表,其中我們傳遞數(shù)據(jù)類型的順序必須符合聲明元組時(shí)的順序。本質(zhì)上,元組的結(jié)構(gòu)需要保持不變。讓我們看一個(gè)例子來更好地理解這個(gè)概念:
let tup: [string, number];
tup = ['Alex', 19087]
在上面的例子中,我們可以看到我們已經(jīng)聲明了一個(gè)具有兩種基本數(shù)據(jù)類型的元組:string和 number 。請注意,當(dāng)我們調(diào)用變量tup時(shí),我們還必須按照聲明的順序傳遞元素類型。本質(zhì)上,我們不能在索引為0處有一個(gè)數(shù)字和索引為1處有一個(gè)字符串,就像這樣:
tup = [19087, 'Alex]
如果我們這樣做了,我們將得到如下所示的錯(cuò)誤:
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']
正如我們從上面前面的例子中看到的,我們正在聲明一個(gè)數(shù)字?jǐn)?shù)組并用值初始化它。只要我們只處理數(shù)字的元素類型,這就可以工作。為了考慮具有多種數(shù)據(jù)類型的數(shù)組,我們可以使用 any 類型或運(yùn)算符,盡管在這種情況下,不能保證數(shù)據(jù)的順序或 | 結(jié)構(gòu),這可能不是我們想要的。
但是,使用元組,我們可以確保數(shù)據(jù)類型和要傳遞的數(shù)據(jù)順序的嚴(yán)格性。元組允許在具有固定數(shù)量的元素的元素類型周圍指定已知類型邊界。
TypeScript 元組用例
由于元組允許我們在數(shù)組中定義固定類型和順序,因此在處理以順序方式相互關(guān)聯(lián)的數(shù)據(jù)(其中順序很重要)時(shí),它們是最好的選擇。這樣,我們可以輕松地以預(yù)定的方式訪問元素,從而使我們期望的響應(yīng)在行為上可預(yù)測。
下面,我們將基于 v4.2 版本在 TypeScript 中探索元組類型的更多用例,這些用例通常圍繞在函數(shù)簽名中提取和傳播參數(shù)列表。
在 REST 參數(shù)中使用元組
REST 參數(shù)語法將參數(shù)收集到單個(gè)數(shù)組變量中,然后展開它們。在最近的 TypeScript 版本中,我們現(xiàn)在可以使用元組類型將 REST 參數(shù)擴(kuò)展為離散參數(shù)。這意味著,當(dāng)類型 tuple 用作REST參數(shù)時(shí),它會(huì)平展到參數(shù)列表的其余部分。
簡單來說,當(dāng) REST 參數(shù)是元組類型時(shí),元組類型可以擴(kuò)展為一系列參數(shù)列表。
請考慮以下示例:
declare function example(...args: [string, number]): void;
REST 參數(shù)將元組類型的元素?cái)U(kuò)展為離散參數(shù)。調(diào)用函數(shù)時(shí), args 表示為 REST 參數(shù)的函數(shù)將展開為與下面的函數(shù)簽名完全相同:
declare function example(args0: string, args1: number): void;
因此,REST 參數(shù)語法收集溢出到數(shù)組或元組中的參數(shù)。總之,元組類型迫使我們將適當(dāng)?shù)念愋蛡鬟f給相應(yīng)的函數(shù)簽名。TypeScript v4.2 增加了在前導(dǎo)或中間元素上展開的功能。這很方便,因?yàn)槟梢允褂?nbsp;REST 參數(shù)在前導(dǎo)或中間參數(shù)上創(chuàng)建可變參數(shù)函數(shù),如下所示:
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 函數(shù)接受具有展開語法的 ... 可變參數(shù)。該參數(shù)是 ,[...Matches[], string]這意味著它需要兩個(gè)或多個(gè)類型的 Matches 元組,后跟一個(gè)字符串。
使用元組展開表達(dá)式
擴(kuò)展語法將數(shù)組或?qū)ο蟮脑財(cái)U(kuò)展為其元素。擴(kuò)展運(yùn)算符還可以擴(kuò)展元組的元素。當(dāng)函數(shù)調(diào)用包含元組類型的擴(kuò)展表達(dá)式作為參數(shù)時(shí),擴(kuò)展表達(dá)式將擴(kuò)展為與元組類型的元素對應(yīng)的參數(shù)序列。讓我們看下面的一個(gè)例子:
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);
注意,從上面的例子中我們可以看到,我們已經(jīng)聲明了一個(gè)元組類型,并將其作為參數(shù)傳遞給函數(shù)簽名。
當(dāng)函數(shù)被調(diào)用時(shí),我們可以將參數(shù)作為文字或通過它們各自的索引傳遞。但是,使用 spread 運(yùn)算符是將元組作為參數(shù)傳遞給函數(shù)調(diào)用的快速而干凈的選項(xiàng)。由于擴(kuò)散運(yùn)算符的性質(zhì),參數(shù)被擴(kuò)展為對應(yīng)于元組類型元素的參數(shù)列表。
解構(gòu)值
因?yàn)樵M是底層的數(shù)組,我們可以像解構(gòu)數(shù)組一樣解構(gòu)它們。重要的是要注意,解構(gòu)變量獲取相應(yīng)元組元素的類型。讓我們看一個(gè)例子:
let tuple: [number, string, boolean];
tuple = [7, "hello", true];
let [a, b, c] = tuple;
// a: number, b: string, c: boolean
TypeScript 元組最佳實(shí)踐
雖然元組有其優(yōu)點(diǎn),但在使用元組之前必須考慮權(quán)衡。元組不如數(shù)組和對象靈活,修改或擴(kuò)展元組可能很麻煩。如果數(shù)據(jù)結(jié)構(gòu)需要頻繁修改或其他屬性,您可能會(huì)發(fā)現(xiàn)數(shù)組或?qū)ο蟾线m。
創(chuàng)建有意義且可重用的元組類型的提示
創(chuàng)建定義明確且可重用的元組類型對于保持清晰度和減少代碼重復(fù)至關(guān)重要。讓我們討論在 TypeScript 中定義和使用元組類型時(shí)要考慮的一些技巧。首先,請確保為元組中的元素分配有意義的名稱,以提高可讀性并幫助其他人了解每個(gè)值的用途。例如,請考慮 [x, y]`` [latitude, longitude] 。
此外,TypeScript 的類型推斷系統(tǒng)可以根據(jù)元組類型的分配值自動(dòng)推斷元組類型。不應(yīng)顯式定義類型,而應(yīng)依靠類型推斷來減少冗余并提高代碼可維護(hù)性。如果元組中的某些元素是可選的,請使用聯(lián)合類型來指示可能存在的元素。靈活性可確保元組類型適應(yīng)多種方案。
當(dāng)元組很復(fù)雜或跨代碼庫的多個(gè)部分重用時(shí),請考慮將它們抽象為接口或類型別名以實(shí)現(xiàn)可重用性,提高代碼可讀性,并允許將來更易于訪問的修改和擴(kuò)展。通過遵循這些提示,您可以創(chuàng)建有意義且可重用的元組類型,以增強(qiáng) TypeScript 程序的清晰度和可維護(hù)性。
使用元組時(shí)要避免的錯(cuò)誤
開發(fā)人員應(yīng)注意一些常見的陷阱,以避免潛在的問題。在本節(jié)中,我們將介紹使用元組時(shí)要避免的一些常見錯(cuò)誤。默認(rèn)情況下,元組是不可變的。嘗試修改元組的值將導(dǎo)致編譯錯(cuò)誤。避免直接更改元組元素;創(chuàng)建具有所需修改的新元組。
請記住,元組依賴于其元素的順序來維護(hù)其結(jié)構(gòu)。意外地對元素重新排序可能會(huì)引入難以發(fā)現(xiàn)的錯(cuò)誤。為了防止這種情況,請使用清晰的描述性變量名稱,并使用解構(gòu)或命名元組元素按名稱訪問值,而不是僅依賴它們的順序。
最后,過度使用元組會(huì)使代碼更難理解和維護(hù)。如果數(shù)據(jù)結(jié)構(gòu)需要頻繁修改,請考慮使用對象或數(shù)組。避免這些錯(cuò)誤將幫助您有效地利用 TypeScript 元組的強(qiáng)大功能并減少潛在的代碼錯(cuò)誤。
總結(jié)
TypeScript 元組就像具有固定數(shù)量的元素的數(shù)組。它們?yōu)槲覀兲峁┝艘粋€(gè)固定大小的容器,可以存儲(chǔ)多種類型的值,其中順序和結(jié)構(gòu)非常重要。當(dāng)我們確切地知道數(shù)組中允許多少種類型時(shí),最好使用此數(shù)據(jù)類型。眾所周知,在原始定義的長度之外分配索引將導(dǎo)致 TypeScript 編譯器出錯(cuò)。
請注意,雖然可以通過元組元素的索引修改元組元素的值,但我們必須確保與聲明元組變量時(shí)提供的類型匹配。這是因?yàn)橐坏┞暶?,我們就無法更改元組中元素的類型甚至大小。
通過我們在這篇文章中強(qiáng)調(diào)的功能,可以設(shè)計(jì)強(qiáng)類型的高階函數(shù),這些函數(shù)可以轉(zhuǎn)換函數(shù)及其參數(shù)列表,并且本質(zhì)上確保一個(gè)健壯的、有據(jù)可查的、可維護(hù)的代碼庫,這是我們使用 TypeScript 的核心。