TypeScript 陣列與元組 (Array and Tuple)
陣列 (Array) 和元組 (Tuple) 是 TypeScript 中用來儲存多個值的資料結構。讓我們來了解如何在 TypeScript 中使用它們。
陣列 (Array)
陣列用來儲存相同型別的多個值。
陣列型別標註
有兩種方式可以標註陣列型別:
// 方式一:型別[] (推薦)
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ['Alice', 'Bob', 'Charlie'];
let flags: boolean[] = [true, false, true];
// 方式二:Array<型別> (泛型語法)
let scores: Array<number> = [90, 85, 92];
let words: Array<string> = ['hello', 'world'];
兩種寫法功能完全相同,選擇你喜歡的風格即可。
空陣列
宣告空陣列時,必須指定型別,否則 TypeScript 會推論為 any[]:
// 明確指定型別
let items: string[] = [];
items.push('item1'); // OK
// items.push(123); // 錯誤:number 不能指派給 string
// 沒有指定型別,推論為 never[] 或 any[]
let unknown = []; // 在 strict 模式下會有問題
唯讀陣列
使用 readonly 關鍵字或 ReadonlyArray<T> 來建立唯讀陣列:
// 使用 readonly 關鍵字
let readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4); // 錯誤:push 不存在於 readonly number[]
// readonlyNumbers[0] = 10; // 錯誤:無法指派給 readonly 屬性
// 使用 ReadonlyArray<T>
let readonlyNames: ReadonlyArray<string> = ['Alice', 'Bob'];
陣列方法的型別
TypeScript 會正確推論陣列方法的回傳型別:
let numbers: number[] = [1, 2, 3, 4, 5];
// map 回傳新陣列,元素型別根據回呼函式決定
let doubled: number[] = numbers.map((n) => n * 2);
let strings: string[] = numbers.map((n) => n.toString());
// filter 回傳相同型別的陣列
let evens: number[] = numbers.filter((n) => n % 2 === 0);
// find 可能回傳 undefined
let found: number | undefined = numbers.find((n) => n > 3);
// reduce 回傳累加器的型別
let sum: number = numbers.reduce((acc, n) => acc + n, 0);
多維陣列
// 二維陣列
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// 三維陣列
let cube: number[][][] = [
[
[1, 2],
[3, 4],
],
[
[5, 6],
[7, 8],
],
];
元組 (Tuple)
元組是固定長度、每個位置有特定型別的陣列。
基本用法
// 定義一個元組:第一個元素是 string,第二個是 number
let person: [string, number] = ['小明', 25];
// 存取元素
let name: string = person[0];
let age: number = person[1];
// 錯誤的用法
// let wrong: [string, number] = [25, "小明"]; // 順序錯誤
// let wrong2: [string, number] = ["小明"]; // 缺少元素
// let wrong3: [string, number] = ["小明", 25, true]; // 多餘元素
元組的解構
let point: [number, number] = [10, 20];
let [x, y] = point;
console.log(x, y); // 10 20
let rgb: [number, number, number] = [255, 128, 0];
let [red, green, blue] = rgb;
可選元素
使用 ? 標記可選元素:
// 第三個元素是可選的
let coordinate: [number, number, number?] = [10, 20];
let coordinate3D: [number, number, number?] = [10, 20, 30];
// 存取可選元素時可能是 undefined
let z: number | undefined = coordinate[2];
剩餘元素 (Rest Elements)
元組可以包含剩餘元素:
// 前兩個元素固定,後面可以有任意數量的 number
let tuple: [string, number, ...boolean[]] = ['hello', 42, true, false, true];
// 函式參數的應用
function log(message: string, ...codes: number[]): void {
console.log(message, codes);
}
唯讀元組
let readonly_tuple: readonly [string, number] = ['小明', 25];
// readonly_tuple[0] = "小華"; // 錯誤:無法指派給 readonly 屬性
// 使用 as const 建立唯讀元組
let point = [10, 20] as const;
// point 的型別是 readonly [10, 20]
具名元組 (Labeled Tuples)
TypeScript 4.0+ 支援為元組元素命名,提高可讀性:
type Point = [x: number, y: number];
type RGB = [red: number, green: number, blue: number];
let point: Point = [10, 20];
let color: RGB = [255, 128, 0];
// 函式參數
function setCoordinate(...args: [x: number, y: number, z?: number]) {
const [x, y, z] = args;
console.log(x, y, z);
}
陣列 vs 元組
| 特性 | 陣列 (Array) | 元組 (Tuple) |
|---|---|---|
| 長度 | 可變 | 固定 |
| 元素型別 | 相同 | 可以不同 |
| 用途 | 同質資料集合 | 固定結構的異質資料 |
| 範例 | [1, 2, 3, 4, 5] | ["小明", 25, true] |
實際應用場景
元組作為函式回傳值
// 回傳多個值
function getNameAndAge(): [string, number] {
return ['小明', 25];
}
const [name, age] = getNameAndAge();
// React 的 useState Hook 就是回傳元組
function useState<T>(initial: T): [T, (value: T) => void] {
let state = initial;
const setState = (value: T) => {
state = value;
};
return [state, setState];
}
const [count, setCount] = useState(0);
座標與顏色
type Coordinate = [number, number];
type RGB = [number, number, number];
type RGBA = [number, number, number, number];
const origin: Coordinate = [0, 0];
const red: RGB = [255, 0, 0];
const transparentBlue: RGBA = [0, 0, 255, 0.5];
鍵值對
type Entry = [string, number];
const entries: Entry[] = [
['apple', 3],
['banana', 5],
['orange', 2],
];
// 轉換為 Map
const map = new Map(entries);
常見錯誤
超出索引存取
let tuple: [string, number] = ['hello', 42];
// TypeScript 4.1+ 會對越界存取報錯
// let third = tuple[2]; // 錯誤:索引 2 不存在於型別 [string, number]
混淆陣列和元組
// 這是陣列,元素可以是 string 或 number
let arr: (string | number)[] = ['hello', 42, 'world', 100];
// 這是元組,嚴格限制位置和型別
let tuple: [string, number] = ['hello', 42];