陣列(Array)是程式開發中最常用的資料結構之一。
它可以把多個值放在同一個變數裡,方便你一次管理大量資料。
TypeScript 的陣列和 JavaScript 很像,但多了「型別檢查」的能力,能在你寫錯的時候提早警告你。
這篇文章會帶你從頭認識 TypeScript 的陣列型別,包括怎麼宣告、怎麼存取元素,以及常用的陣列方法。
方括號語法宣告陣列
TypeScript 宣告陣列最常見的寫法,是在某個型別後面加上一對方括號 [],用來表示「這是一個由該型別組成的陣列」。
舉例來說,number 代表「數字」這個型別,那麼 number[] 就代表「一個裡面全部都是數字的陣列」。
同理,string 代表「字串」,string[] 就代表「一個裡面全部都是字串的陣列」。
let arr: number[] = [1, 2, 3];
let names: string[] = ["Tony", "Steve", "Joan"];型別標註寫在變數名稱後面,中間用冒號 : 隔開,完整格式是 變數名: 型別[]。
如果你放了不對的型別進去,TypeScript 會直接報錯。
這代表你不用等到程式執行才發現 bug,在編輯器裡就能即時看到錯誤,這就是它比 JavaScript 方便的地方。
方括號語法搭配不同的基本型別
基本型別(Primitive Type)指的是程式語言內建、最基礎的資料型別,例如數字、字串、布林值。
它們是組成複雜資料的最小單位。
方括號語法可以搭配這些基本型別,宣告出對應的陣列。
只要把基本型別放在 [] 前面就可以了。
最常見的有以下三種。
數字陣列:
let arr: number[] = [1, 2, 3];number[] 代表這個陣列裡面只能放數字,例如 1、2、3 這種整數,或是 3.14 這種小數都可以。
如果你試著放入字串或其他型別,TypeScript 就會報錯。
字串陣列:
let names: string[] = ["Tony", "Steve", "Joan"];string[] 代表這個陣列裡面只能放字串。
字串要用引號包起來,單引號 ' 或雙引號 " 都可以。
布林值陣列:
let flags: boolean[] = [true, false, true];boolean[] 代表這個陣列裡面只能放布林值。
布林值只有兩個可能:true(真)和 false(假),通常用來表示開關、是非這類只有兩種狀態的資料。
這三種寫法的規則都一樣:把基本型別接上 [],就得到對應的陣列型別。
換成其他基本型別也是同樣的道理。
泛型語法宣告陣列
除了方括號語法,TypeScript 還提供另一種寫法:泛型語法(Generic)。
「泛型」這個名字裡的「泛」,意思是「廣泛、不特定」。
它指的是一種「先把型別空著、等用的時候再填進去」的設計。
換句話說,泛型本身不綁定特定型別,而是預留一個空位,讓你決定要放什麼型別進去。
以陣列來說,TypeScript 內建一個叫 Array 的泛型。
Array 本身只表達「這是一個陣列」,但沒有說明陣列裡裝的是什麼。
你要在角括號 <> 裡填上型別,才算把這個空位補完整。
可以把還沒填型別的 Array 想像成這樣,角括號裡是一個等待填入的空位:
把 number 填進這個空位,就變成 Array<number>,意思是「一個裝數字的陣列」。
let arr: Array<number> = [1, 2, 3];把 string 填進去,就變成 Array<string>,意思是「一個裝字串的陣列」。
let names: Array<string> = ["Tony", "Steve", "Joan"];這種寫法的效果和方括號語法完全一樣。
泛型語法搭配不同的基本型別
泛型語法同樣可以搭配各種基本型別。
數字陣列:
let arr: Array<number> = [1, 2, 3];字串陣列:
let names: Array<string> = ["Tony", "Steve", "Joan"];布林值陣列:
let flags: Array<boolean> = [true, false, true];兩種語法的效果完全一樣,選哪種都可以。
大多數開發者習慣用方括號語法,因為比較簡潔。
先宣告、後賦值
到目前為止的範例,都是在宣告陣列的同一行就把值給它。
但這兩件事其實可以分開做:先宣告變數的型別,之後再賦值。
let names7: string[];
names7 = ["Tony", "Steve", "Joan"];第一行 let names7: string[]; 只做了一件事:告訴 TypeScript「names7 這個變數未來會是一個字串陣列」。
此時 names7 還沒有任何值。
第二行 names7 = ["Tony", "Steve", "Joan"]; 才真正把陣列的內容填進去。
這樣拆開寫的好處是,型別已經先確定了,之後不管你賦什麼值給 names7,TypeScript 都會用 string[] 這個型別來檢查。
如果你試著賦一個數字陣列給它,TypeScript 就會報錯。
這種寫法在「值要根據條件才能決定」的情境特別實用。
let names7: string[];
if (isLoggedIn) {
names7 = ["Tony", "Steve"];
} else {
names7 = ["Guest"];
}這個例子先宣告 names7 是字串陣列,再根據 isLoggedIn 的結果決定要填入哪一組值。
不論走哪個分支,TypeScript 都會確保填進去的是字串陣列。
用索引讀取陣列元素
宣告好陣列之後,最基本的操作就是讀取裡面的某一個元素。
要做到這件事,你需要用到「索引」(index)。
陣列裡的每個元素都有一個編號,這個編號就是索引。
索引從 0 開始算,不是從 1 開始。
也就是說,第一個元素的索引是 0,第二個是 1,以此類推。
把索引用方括號 [] 包起來,接在陣列變數後面,就能取出對應位置的元素。
let names: string[] = ["Tony", "Steve", "Joan"];
console.log(names[0]); // 印出 "Tony"
console.log(names[2]); // 印出 "Joan"names[0] 取得索引 0 的元素,也就是第一個元素 "Tony"。
names[2] 取得索引 2 的元素,也就是第三個元素 "Joan"。
剛接觸的人很容易把索引和「第幾個」搞混,記住「索引比順序少 1」就不會錯了。
TypeScript 陣列常用方法
索引一次只能讀取一個元素。
如果要對整個陣列做操作,例如新增、刪除、排序,就要用到「陣列方法」。
陣列方法是 TypeScript 內建、可以直接呼叫的功能,用 陣列.方法名() 的形式使用。
TypeScript 的陣列繼承了 JavaScript 所有的陣列方法,以下是幾個最常用的:
pop():移除陣列最後一個元素,並回傳被移除的元素push():在陣列最後面新增一個或多個元素sort():對陣列元素進行排序concat():合併兩個以上的陣列,回傳新陣列indexOf():搜尋某個元素在陣列中的索引位置splice():在指定位置新增或移除元素forEach():對陣列中的每個元素執行一次指定的函式
以下用 pop() 當例子,實際看一個方法怎麼運作。
let names7: string[];
names7 = ["Tony", "Steve", "Joan"];
console.log(names7.pop()); // 印出 "Joan"
console.log(names7); // 印出 ["Tony", "Steve"]pop() 做了兩件事:
- 把陣列最後一個元素(
"Joan")移除 - 回傳被移除的那個元素
所以第一個 console.log 印出的是被移除的 "Joan"。
第二個 console.log 印出的是移除之後的陣列,只剩下 ["Tony", "Steve"]。
其他方法的用法也是同樣的模式:用 陣列.方法名() 呼叫,有些會回傳結果,有些會直接改變原本的陣列。
結語
陣列是 TypeScript 裡最基礎、也最常用的資料結構之一。
只要掌握「怎麼宣告」「怎麼存取」「有哪些方法可以用」這三件事,你就能應付絕大多數需要處理多筆資料的場景。
這篇文章示範的都是單一型別的陣列。
實際開發時,你還會遇到需要在同一個陣列裡混合多種型別的情況,那就會用到「聯合型別」(Union Type)這個更進階的主題。
建議你先動手把這篇的範例都實際打過一遍,熟悉了基本操作之後,再往更進階的型別概念前進。