1. 程式人生 > >深入淺出TypeScript(3)- 函式過載和泛型

深入淺出TypeScript(3)- 函式過載和泛型

面向物件特性中,最根本的就是面向物件的三大基本特徵:封裝、繼承、多型。同時,TypeScript中也存在多型的使用,比如函式過載,今天我們先看一下函式過載以及泛型的概念。

什麼是函式過載

簡單來說,函式過載具有兩個特徵:名稱相同,引數不同(引數型別、個數不同。)所以,函式過載的解釋應該是具備不同引數的同名函式。注意:函式過載是多型的一種體現。

函式過載的宣告和實現

TypeScript中,函式過載主要包括兩部分:函式宣告,和函式實現。函式宣告主要是TSC解析的一種宣告體現,實際編譯中,並不會編譯成具體程式碼。我們可以通過TypeScript的playground來檢視。

1、引數不同的函式過載

加入我們有一個列印函式,可以列印輸入的一個string資訊,我們可以將函式宣告如下:

// 函式宣告
function print(info: string): void;

  

而還有另一種情況,就是輸入的有可能是兩個string型別的引數,我們都需要列印下來,於是我們的函式宣告可以是這樣:

// 函式宣告
function print(info: string, message: string): void;

  

而當這兩種宣告,同時存在TypeScript的宣告檔案中,我們就需要用函式過載來實現,這是JavaScript沒有的特性。

而實現函式過載的要求就是,我們要在一個更為寬泛的範圍去實現函式過載,所以,TypeScript中的我們實現print函式如下:

// 在更寬泛的範圍,我們用可選引數來實現過載
function print(info: string, message ?: string) {
    let printValue: string = info;
    if(message){
        printValue += message;
    }
    console.log(printValue);
}

  

2、引數個數相同,但型別不同的函式過載

函式過載的第二種情況,引數個數相同,但是引數型別不一樣,這種情況下也可以通過過載來實現。

比如,上述列印資訊的函式,有可能接受的輸入是一個string字串,也有可能輸入接受的是一個number型別的數字,那麼我們第一步的函式宣告便是如下:

function print(info: string): void;
function print(num: number): void;

  

從上可以看到,我們的函式宣告中,引數的型別是不同的,在這種情況下,TypeScript是如何在一個寬泛的範圍內實現呢?這裡就要用到聯合型別,如下所示:

function print(message: string | number) {
    console.log(message)
}

  

函式過載的總結

從我們實現兩個函式過載的例子可以看出,我們在TypeScript中實現函式過載的方式分別是利用了TypeScript中的兩個型別特性:可選型別以及聯合型別。

所以,如果從便捷的角度來講,我們如果是遇到了類似的實現,其實可以直接使用可選引數和聯合型別來實現自己想要的函式效果。

泛型

在函式過載的不同引數型別,相同引數個數的過載中,我們介紹了它的過載實現方式,利用聯合型別來實現,但是如果要打印出來的型別有很多,那麼我們最終只能用any型別來實現print函數了。

但是,如果用any型別實現一個可以列印任意值的print函式,這樣又讓我們的函式變得型別缺失,這個時候,泛型這種解決方案也就應運而生。

什麼是泛型

泛型指的是一種情況:定義是可以是任意型別,但是在編譯的時候,必須有明確的型別。

有點繞,那麼我們用泛型來實現上述第二個函式過載的例子,結合這個例子,可以體會一下這句話的含義。

function print<T>(message: T) {
     console.log(message);
}

  

在這個函式中,泛型表示的方式是:函式名稱<泛型引數>(arg: 泛型引數)。

這個函式在宣告之後,函式型別是一個泛型。我們可以傳遞任意的型別引數到print函式中,但是當我們傳遞一個string型別的時候,這個函式便是一個string型別的函數了,已經在tsc編譯階段開始明確指定型別,這是和any函式所不一樣的地方。

泛型的好處

首先,我們不用定義過多的聯合型別來讓函式變得複雜而又冗長,如:

function print(arg: string | number | boolean | array | 自定義型別) {
    // 我們應該儘量避免多型別的傳值函式,此時我們應該用泛型來實現。
}

  

其次,泛型可以是任何型別,但是在編譯時一定是型別確定的。而且泛型也可以有繼承屬性,可以繼承介面獲取更多的型別定義等。

function print<T extends Interface> (arg: T) {
    // 通過繼承,來讓泛型有更多的可變性。
}

  

最後,類型別名也可以是泛型,如我們可以做如下型別定義:

type Person<T> = { age: T } 

  

泛型總結

總體來說,利用泛型,也是為了更準確的讓我們使用型別思維,是為了更準確的描述引數、或者宣告的型別準確性,如果能夠熟練的掌握泛型,那麼在TypeScript的開發中,將會有不一樣的體驗。

而常常以型別思維去思考JavaScript中的函式或者變數,我們也就會減少很多因為型別方面的犯錯,使得我們的專案不僅更好的測試,也會更少的出錯。

不得不說,在大前端領域,型別思維的缺失的確是個普遍現象,如果將型別思維撿起來,將會是一個可能存在著痛苦的過程,但是我相信,如果你做到了,那麼你不僅會在開發程式碼的時候會更謹慎,能開發出更優秀的應用程式,還會體驗到前端行業別樣的魅力。

我的部落格地址:http://www.gaoyunjiao.fun/?p