TypeScript中函式過載寫法,你在第幾層!

前端小智發表於2021-12-17
作者: dmitripavlutin
譯者:前端小智
來源:dmitripavlutin
有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

大多數函式接受一組固定的引數。

但有些函式可以接受可變數量的引數,不同型別的引數,甚至可以根據你呼叫函式的方式返回不同的型別。為了註釋這樣的函式,TypeScript 提供了函式過載功能。

1. 函式簽名

我們先來考慮一個函式,它返回給一個特定的人的問候資訊。

function greet(person: string): string {
  return `Hello, ${person}!`;
}

上面的函式接受1個字元型別的引數:人的名字。呼叫該函式是非常簡單的:

greet('World'); // 'Hello, World!'

如果你想讓 greet()函式更加靈活,怎麼辦?例如,讓它另外接受一個要問候的人的列表。

這樣的函式將接受一個字串或字串陣列作為引數,並返回一個字串或字串陣列。

如何對這樣的函式進行註釋?有2種方法。

第一種方法很簡單,就是通過更新引數和返回型別直接修改函式簽名。下面重構後greet()的樣子:

function greet(person: string | string[]): string | string[] {
  if (typeof person === 'string') {
    return `Hello, ${person}!`;
  } else if (Array.isArray(person)) {
    return person.map(name => `Hello, ${name}!`);
  }
  throw new Error('Unable to greet');
}

現在我們可以用兩種方式呼叫 greet():

greet('World');          // 'Hello, World!'
greet(['小智', '大冶']); // ['Hello, 小智!', 'Hello, 大冶!']

直接更新函式簽名以支援多種呼叫方式是一種常見的好方法。

然而,在某些情況下,我們可能需要採用另一種方法,分別定義你的函式可以被呼叫的所有方式。這種方法被稱為函式過載

2.函式過載

第二種方法是使用函式過載功能。當函式簽名相對複雜且涉及多種型別時,我推薦使用這種方法。

定義函式過載需要定義過載簽名和一個實現簽名。

過載簽名定義函式的形參和返回型別,沒有函式體。一個函式可以有多個過載簽名:對應於呼叫該函式的不同方式。

另一方面,實現簽名還具有引數型別和返回型別,而且還有實現函式的主體,且只能有一個實現簽名。

// 過載簽名
function greet(person: string): string;
function greet(persons: string[]): string[];
 
// 實現簽名
function greet(person: unknown): unknown {
  if (typeof person === 'string') {
    return `Hello, ${person}!`;
  } else if (Array.isArray(person)) {
    return person.map(name => `Hello, ${name}!`);
  }
  throw new Error('Unable to greet');
}

greet() 函式有兩個過載簽名和一個實現簽名。

每個過載簽名都描述了可以呼叫該函式的一種方式。就 greet()函式而言,我們可以用兩種方式呼叫它:用一個字串引數,或用一個字串陣列引數。

實現簽名 function greet(person: unknown): unknown { ... } 包含了該函式如何工作的適當邏輯。

現在,和上面一樣,可以用字串或字串陣列型別的引數來呼叫 greet()

greet('World');          // 'Hello, World!'
greet(['小智', '大冶']); // ['Hello, 小智!', 'Hello, 大冶!']

2.1 過載簽名是可呼叫的

雖然實現簽名實現了函式行為,但是它不能直接呼叫。只有過載簽名是可呼叫的。

greet('World');          // 過載簽名可呼叫
greet(['小智', '大冶']); // 過載簽名可呼叫

const someValue: unknown = 'Unknown';
greet(someValue);       // Implementation signature NOT callable

// 報錯
No overload matches this call.
  Overload 1 of 2, '(person: string): string', gave the following error.
    Argument of type 'unknown' is not assignable to parameter of type 'string'.
  Overload 2 of 2, '(persons: string[]): string[]', gave the following error.
    Argument of type 'unknown' is not assignable to parameter of type 'string[]'.

在上面的示例中,即使實現簽名接受unknown引數,也不能使用型別為 unknown (greet(someValue)) 的引數呼叫 greet() 函式。

2.1 實現簽名必須是通用的

// 過載簽名
function greet(person: string): string;
function greet(persons: string[]): string[]; 
// 此過載簽名與其實現簽名不相容。

 
// 實現簽名
function greet(person: unknown): string {
  // ...
  throw new Error('Unable to greet');
}

過載簽名函式 greet(person: string[]): string[] 被標記為與greet(person: unknown): string不相容。

實現簽名的 string 返回型別不夠通用,不能與過載簽名的 string[] 返回型別相容。

3.方法過載

雖然在前面的例子中,函式過載被應用於一個普通函式。但是我們也可以過載一個方法

在方法過載區間,過載簽名和實現簽名都是類的一部分了。

例如,我們實現一個 Greeter類,有一個過載方法greet()

class Greeter {
  message: string;
 
  constructor(message: string) {
    this.message = message;
  }
 
  // 過載簽名
  greet(person: string): string;
  greet(persons: string[]): string[];
 
  // 實現簽名
  greet(person: unknown): unknown {
    if (typeof person === 'string') {
      return `${this.message}, ${person}!`;
    } else if (Array.isArray(person)) {
      return person.map(name => `${this.message}, ${name}!`);
    }
    throw new Error('Unable to greet');
  }

Greeter 類包含 greet() 過載方法:2個過載簽名描述如何呼叫該方法,以及包含正確實現的實現簽名

由於方法過載,我們可以用兩種方式呼叫 hi.greet():使用一個字串或使用一個字串陣列作為引數。

const hi = new Greeter('Hi');
 
hi.greet('小智');       // 'Hi, 小智!'
hi.greet(['王大冶', '大冶']); // ['Hi, 王大冶!', 'Hi, 大冶!']

4. 何時使用函式過載

函式過載,如果使用得當,可以大大增加可能以多種方式呼叫的函式的可用性。這在自動補全時特別有用:我們會在自動補全中列出所有可能的過載記錄。

然而,在某些情況下,建議不要使用函式過載,而應該使用函式簽名。

例如,不要對可選引數使用函式過載:

// 不推薦做法
function myFunc(): string;
function myFunc(param1: string): string;
function myFunc(param1: string, param2: string): string;
function myFunc(...args: string[]): string {
  // implementation...
}

在函式簽名中使用可選引數是足夠的:

// 推薦做法
function myFunc(param1?: string, param2: string): string {
  // implementation...
}

5.總結

TypeScript中的函式過載讓我們定義以多種方式呼叫的函式。

使用函式過載需要定義過載簽名:一組帶有引數和返回型別的函式,但沒有主體。這些簽名表明應該如何呼叫該函式。

此外,你必須寫出函式的正確實現(實現簽名):引數和返回型別,以及函式體。請注意,實現簽名是不可呼叫的。

除了常規的函式之外,類中的方法也可以過載。


程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原文:https://dmitripavltin.com/typ...

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章