TypeScript 泛型

方法簽名解析

private repeatArrayToLength<T>(arr: T[], targetLength: number): T[]

泛型 <T> 詳細說明

什麼是泛型?

泛型 (Generics) 是 TypeScript 的一個強大特性,允許我們編寫可重複使用的程式碼,同時保持型別安全。

<T> 的含義

<T>  // T 是一個型別變數 (Type Variable)
  • T 代表 "Type",是一個佔位符

  • 可以用任何名稱,如 <U><K><V>,但 <T> 是慣例

  • 在方法被呼叫時,T 會被具體的型別取代

泛型的運作原理

// 方法定義
private repeatArrayToLength<T>(arr: T[], targetLength: number): T[]

// 呼叫時,TypeScript 會推斷或明確指定 T 的型別

使用範例與型別推斷

範例 1:字串陣列

const colors = ['red', 'blue', 'green'];
const repeatedColors = this.repeatArrayToLength(colors, 5);

// TypeScript 推斷:
// T = string
// arr: string[]
// 返回值: string[]
// repeatedColors = ['red', 'blue', 'green', 'red', 'blue']

範例 2:數字陣列

const numbers = [1, 2, 3];
const repeatedNumbers = this.repeatArrayToLength(numbers, 7);

// TypeScript 推斷:
// T = number
// arr: number[]
// 返回值: number[]
// repeatedNumbers = [1, 2, 3, 1, 2, 3, 1]

範例 3:物件陣列

interface Color {
  name: string;
  hex: string;
}

const colorObjects: Color[] = [
  { name: 'red', hex: '#FF0000' },
  { name: 'blue', hex: '#0000FF' }
];

const repeatedColorObjects = this.repeatArrayToLength(colorObjects, 3);

// TypeScript 推斷:
// T = Color
// arr: Color[]
// 返回值: Color[]
// repeatedColorObjects = [
//   { name: 'red', hex: '#FF0000' },
//   { name: 'blue', hex: '#0000FF' },
//   { name: 'red', hex: '#FF0000' }
// ]

範例 4:明確指定型別

// 雖然不常見,但可以明確指定型別
const result = this.repeatArrayToLength<string>(['a', 'b'], 4);
// 明確告訴 TypeScript:T = string

沒有泛型的版本對比

不使用泛型(型別不安全)

// ❌ 不好的寫法
private repeatArrayToLength(arr: any[], targetLength: number): any[] {
  // ...
}

// 問題:失去型別安全性
const colors = ['red', 'blue'];
const result = this.repeatArrayToLength(colors, 3);
// result 的型別是 any[],無法享受 TypeScript 的型別檢查

使用泛型(型別安全)

// ✅ 好的寫法
private repeatArrayToLength<T>(arr: T[], targetLength: number): T[] {
  // ...
}

// 優點:保持型別安全性
const colors = ['red', 'blue'];
const result = this.repeatArrayToLength(colors, 3);
// result 的型別是 string[],享受完整的型別檢查和智能提示

Array.from 詳解

基本語法

Array.from(arrayLike, mapFunction?, thisArg?)

在我們方法中的使用

return Array.from(
  { length: targetLength },        // arrayLike: 類陣列物件
  (_, index) => arr[index % arr.length]  // mapFunction: 映射函數
);

分解說明

1. 建立類陣列物件

{ length: targetLength }
// 例如 targetLength = 5 時
// { length: 5 }
// 這會被 Array.from 視為有 5 個元素的類陣列

2. 映射函數

(_, index) => arr[index % arr.length]

// 參數說明:
// _: 當前元素值(我們不需要,所以用 _ 表示忽略)
// index: 當前索引 (0, 1, 2, 3, 4...)

// 範例:arr = ['red', 'blue', 'green'], targetLength = 7
// index = 0: arr[0 % 3] = arr[0] = 'red'
// index = 1: arr[1 % 3] = arr[1] = 'blue' 
// index = 2: arr[2 % 3] = arr[2] = 'green'
// index = 3: arr[3 % 3] = arr[0] = 'red'     ← 循環開始
// index = 4: arr[4 % 3] = arr[1] = 'blue'
// index = 5: arr[5 % 3] = arr[2] = 'green'
// index = 6: arr[6 % 3] = arr[0] = 'red'
// 結果: ['red', 'blue', 'green', 'red', 'blue', 'green', 'red']

模運算 (%) 的循環邏輯

// 模運算的循環特性
arr.length = 3

index % arr.length 的結果:
0 % 3 = 0  → arr[0]
1 % 3 = 1  → arr[1] 
2 % 3 = 2  → arr[2]
3 % 3 = 0  → arr[0]  ← 重新開始
4 % 3 = 1  → arr[1]
5 % 3 = 2  → arr[2]
6 % 3 = 0  → arr[0]  ← 再次循環

完整執行流程示範

// 呼叫範例
const colors = ['red', 'blue', 'green'];
const result = this.repeatArrayToLength(colors, 5);

// 執行步驟:
// 1. T 被推斷為 string
// 2. 邊界檢查:arr.length = 3 > 0, targetLength = 5 > 0 ✓
// 3. Array.from 執行:
//    { length: 5 } 建立 5 個位置的類陣列
//    index = 0: colors[0 % 3] = colors[0] = 'red'
//    index = 1: colors[1 % 3] = colors[1] = 'blue'
//    index = 2: colors[2 % 3] = colors[2] = 'green'
//    index = 3: colors[3 % 3] = colors[0] = 'red'
//    index = 4: colors[4 % 3] = colors[1] = 'blue'
// 4. 返回:['red', 'blue', 'green', 'red', 'blue']

Last updated