iT邦幫忙

2024 iThome 鐵人賽

DAY 9
1
JavaScript

TypeScript 初學者也能看的學習指南系列 第 9

TypeScript 初學者也能看的學習指南 09 - Function Overloads 函式重載

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20240919/20149362NadoH0nnx8.png
接續 第 8 天的 - Function
今天來談談一個在 JavaSceipt 中沒有,在 TypeScript 中卻有的概念 - Function Overloads(函式重載)
你會了解到「Function Overloads 是什麼」、它想要解決什麼問題以及實作時我們該注意什麼


Function Overloads 是什麼

Function Overloads 中文可以翻成「函式重載」、「函式過載

不過在談 函式重載(Function Overloads) 之前,先來談談 函式簽名(Function Signatures )
他們有著緊密的關係

函式簽名(Function Signatures)

第一次看到這個專有名詞時整個霧颯颯@@ 想說這是什麼
函式簽名」通常由函式的名稱參數組合,以及回傳值的型別所組成,就像是一個集合體,裡面放了函式的相關資訊。它幫我們確定了在調用函式時的預期方式以及需要傳入的參數數量和型別

JavaScript 本身是一個動態語言,它並不像強型別語言(如:C#)中有「函式簽名」的概念
但在 TypeScript 中,「函式簽名」變得非常重要!
在不同的語言裡,「函式簽名」的定義和細節略有不同,但以 TypeScript 為例,包含以下幾個部分:

  1. 函式名稱
  2. 參數組合:包含數量、型別和順序
  3. 回傳值型別
function add(a: number, b: number): number {
  return a + b;
}

以上方這例子來說

  1. 函式名稱:add
  2. 參數組合:(a: number, b: number)
  3. 回傳值型別:number

函式重載(Function Overloads)

In TypeScript, we can specify a function that can be called in different ways by writing overload signatures. To do this, write some number of function signatures (usually two or more), followed by the body of the function

節錄自官方文件

上方的文字主要是在說,每一個簽名都定義了一種可能的參數列表,函式在重載時,利用簽章的不同來區分到底是呼叫了哪個方法。 Function Overloads 允許同一個函式名稱具有多個不同的函式簽名(通常是 2 個以上),這也代表眼前的某個函式,可以根據不同數量、型別的參數來執行不同的行為

函式重載(Function Overloads) 可以分為兩大部分:

  1. 重載簽名(Overload Signatures):函式可以接受哪些不同的參數組合,這些簽名「不包含」任何程式邏輯
  2. 實施簽名(Implementation Signature):函式的執行,包含實際邏輯,用來處理所有重載簽名中定義的參數組合
// 重載簽名 overload signatures
function greet(name: string): string;
function greet(age: number): string;

// 實施簽名 Implementation Signature
function greet(value: any): string {
    if (typeof value === "string") {
        return `Hello ${value}!`;
    } else if (typeof value === "number") {
        return `You are ${value} years old.`;
    }
    return;
}


console.log(greet("Alice"));  // Hello Alice!
console.log(greet(25));       // You are 25 years old.

可以看到滑鼠游標放到函式名稱上會出現 +1 overloads 的提示字樣,這是在告訴你還有另外 1 個 function overloads
https://ithelp.ithome.com.tw/upload/images/20240919/20149362bbTs1wbGcR.png

Function Overloads 解決什麼問題

  1. 不同參數組合導致重複寫一樣的程式碼
    當函式需要處理不同的參數時,開發者需要為每一種參數寫不同的函式,這會導致寫了更多重複的程式碼,增加維護難度
// 沒有重載的情況
function printNumber(num: number) {
  console.log(num);
}

function printString(str: string) {
  console.log(str);
}
  1. 避免不必要的類型檢查
    在 JavaScript 中,通常需要在函式內進行類型檢查來處理不同的參數類型,這使得程式碼更加冗長
// 沒有重載的情況
function process(value: string | number) {
  if (typeof value === 'string') {
    console.log(`String: ${value}`);
  } else {
    console.log(`Number: ${value}`);
  }
}
  1. 讓函式的調用更清晰、容易理解
    當函式需要根據不同的參數類型或數量來執行不同的商業邏輯時,有函式重載,函式的調用方式也變得更直觀、更好理解,TypeScript 編譯器會提示哪些參數是正確的組合,減少誤用的風險

假設我們正在寫一個用於計算商品的價格的函式,它需要根據不同情況提供不同的計算方式和結果:

  • 如果只有商品名稱,則使用商品的預設價格
  • 如果提供了商品數量,則按商品數量來計算總價
  • 如果提供了數量和折扣碼,則還要扣除折扣的金額

如果不用函式重載,這個函式的邏輯甚至長相可能會變得更複雜,因為會需要處理多個可選參數

// 沒有重載的情況
function calculatePrice(item: string, quantity?: number, discountCode?: string): number {
  let price = 100; // 預設價格

  if (quantity) {
    price *= quantity; // 總價
  }

  if (discountCode) {
    price *= 0.9; // 折扣碼統一打九折
  }

  return price;
}

calculatePrice("Laptop"); // 100
calculatePrice("Laptop", 2); // 200
calculatePrice("Laptop", 2, "2024HappyHour"); // 180

如何寫出一個好的 Overloads

❌ Don't ❌

  1. 避免為了重載而重載
    避免過度重載同一個函式,建議當每個重載簽名代表不同的邏輯或場景時再使用,過多的重載反而本末倒置了
function print(value: number): void;
function print(value: string): void;
function print(value: boolean): void;
function print(value: null): void; // 過度重載
  1. 避免讓簽名過於相似缺乏區別
function test(x: number, y: number): void;
function test(x: number, y: number, z: number): void; // 這兩個簽名過於相似

✅ Do's ✅

  1. 可為不同情境設置不同的重載簽名
    使用重載來區分不同的業務邏輯,而不是僅僅區分型別也是在區分不同的使用情境
function getUserInfo(id: number): User;
function getUserInfo(email: string): User;
  1. 盡量使用具體型別,避免模糊的簽名
function processOrder(id: number): void;
function processOrder(name: string): void;
  1. 依情況適時使用「聯合類型」
    當參數有固定的範圍時,可以使用「聯合類型」來限制參數的值
function setStatus(status: 'active' | 'inactive' | 'pending'): void;

追加後記

我在寫這篇文章時,心中有冒出一個疑問~

Q: 重載 不是也是寫了很多重複的 code 嗎?

A: 重載確實會在定義多個簽名時產生重複的 code。但這些簽名本身並不包含邏輯,而是提供了編譯器型別檢查的一個判斷依據,真正的邏輯只需要在「實施簽名(Implementation Signature)」中實現一次就好

這樣雖然看起來是多次聲明,但重載實際上減少了在實際程式碼中的重複邏輯

  • 重載簽名 vs 重複邏輯
    重載簽名的重複,並不表示邏輯的重複。雖然可能需要寫多個簽名來表達不同的參數組合,但所有這些簽名最後只需要一個具體的函式來實現。因此,重複的程式邏輯並不存在

總結

  • 函式簽名定義了函式的「參數型別」、「數量」、「回傳型別」等,這是函式重載的判斷基礎
  • Function Overloads (函式重載)允許同一個函式根據不同簽名接受不同類型或數量的參數
  • 在 TypeScript 等靜態型別語言中,函式簽名和重載是緊密相關的,而在 JavaScript 中則沒有這種機制,但可以用型別判斷來模擬類似的行為

每天講的內容有推到 github 上喔

References


上一篇
TypeScript 初學者也能看的學習指南 08 - Function 函式
下一篇
TypeScript 初學者也能看的學習指南 10 - Enum 列舉
系列文
TypeScript 初學者也能看的學習指南16
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言