TypeScript學習筆記(三)泛型、模塊化和命名空間

一、泛型

1. 泛型函數

function getMin<T>(arr: T[]):T {
    if(arr.length === 0) {
        throw new Error("輸入的數組沒有元素"); 
    }
    let res: T = arr[0];
    for (let i = 1; i < arr.length; i++) {
        if(arr[i] < res) {
            res = arr[i];
        }
    }
    return res;
}

C++ 的泛型函數非常類似

2. 泛型類

下面這個類可以求出當前列表中最小的元素

class MinClass<T> {
    private dataArr: T[] = [];
    public add(...val: T[]): void {
        this.dataArr.push(...val);
    }
    public getMin(): T {
        if(this.dataArr.length === 0) {
            throw new Error("當前數組沒有元素"); 
        }
        let res: T = this.dataArr[0];
        for (let i = 1; i < this.dataArr.length; i++) {
            if(this.dataArr[i] < res) {
                res = this.dataArr[i];
            }
        }
        return res;
    }
}

3. 泛型接口

寫法一

interface ConfigFn {
    <T>(value:T):T;
}
let getData:ConfigFn = function<T>(value:T):T {
    return value;
}
getData<string>("張三");

寫法二

interface ConfigFn<T> {
    (value:T): T;
}
function getData<T>(value:T) {
    return value;
}
var myGetData:ConfigFn<string> = getData;
myGetData("20");

兩種寫法的區別

  • 寫法一:一般用於在使用時才規定泛型的類型,用法類似泛型函數
  • 寫法二:一般用於約束函數參數傳遞的類型,類似 Java 中的:
public void func(List<Integer> array){
  ...
}

這樣的參數約束。

二、模塊化

TypeScript 中一個 ts 文件中的函數和變量都是該文件私有的,只有通過 export 輸出才能被外部調用。

1. export寫法一

例如以下情況,我們希望暴露兩個函數供外部調用,可以這樣寫:

// db.ts
export function getData():any[] {
    console.log("獲取數據庫的數據");
    return [
        { title:"121312" },
        { title:"123123" }
    ]
}

export function save() {
    console.log("保存數據成功")
}

在需要使用的地方使用 import 關鍵字進行引入,語法如下:

import { aaa,bbb } from "模塊的路徑";

注意,引入時不需要加最後的 .ts 後綴名,示例如下:

// index.ts
import { getData,save } from "./05module";
let data:any[] = getData();
console.log(data);
save();

因為模塊的輸出默認是以對象形式進行的(即所有export的東西都被封裝到一個對象中),所以如果我們希望直接使用其他模塊的方法或字段,需要使用 {} 運算符取出來再使用。

2. export寫法二

我們也可以將所有的東西統一輸出,避免編寫多個 export

function getData():any[] {
    console.log("獲取數據庫的數據");
    return [
        { title:"121312" },
        { title:"123123" }
    ]
}

function save() {
    console.log("保存數據成功")
}

// 將所有需要輸出的東西包裝成對象
export {
    getData,
    save
}

3. 為引入的方法或變量起別名

當我們覺得引入的方法或變量的名字使用起來不太方便等時可以為它們起別名,使用 as 關鍵字:

import { save as saveData } from "./05module";
saveData();

4. export default的使用

一個模塊中 export default 只能使用一次,一般當只希望暴露一個方法時可以使用,例如:

// db.ts
function getData():any[] {
    console.log("獲取數據庫的數據");
    return [
        { title:"121312" },
        { title:"123123" }
    ]
}
export default getData;

而此時的引入也不相同:

import getData from "./05module";
let data:any[] = getData();
console.log(data);

在引入的時候我們不需要使用 {} 操作符進行獲取了,此外我們 import 後面的變量名也不需要和export的變量相同:

import hello from "./05module";
let data:any[] = hello();
console.log(data);

這樣也是可以得到正確結果的

tips:使用 export 來導出一個類的操作和導出變量和方法的操作都是相同的,因為編譯成 js 代碼後,一個類本質上就是一個構造函數,在 js 中函數就是一種變量類型,因此操作都是一致的。

三、命名空間

語法:

namespace 命名空間名 {
  ...
}

作用:一個模塊內可以有多個命名空間,不同命名空間中的相同標識符的變量、函數和類等等可以重名不發生衝突

導出命名空間:

export namespace A {
  ...
}

引入命名空間的方法和引入變量及函數的方式一致。

tips:命名空間中的內容默認都是私有的,即使導出了命名空間,但默認還是無法直接使用到命名空間內部的函數的,需要將它們也export出來。

示例:

export namespace A {
    interface Animal {
        name: string;
        eat():void;
    }
    export class Dog implements Animal {
        name: string;
        constructor(name:string) {
            this.name = name;
        }
        eat(): void {
            console.log(`小狗${this.name}在吃狗糧`);
        }
    }
    export class Cat implements Animal {
        name: string;
        constructor(name:string) {
            this.name = name;
        }
        eat(): void {
            console.log(`小貓${this.name}在吃魚`);
        }
    }
}
  
let dog = new A.Dog("小黑");
dog.eat();

image-20210810125654550