爲vue3學點typescript, 泛型

往期

第一課, 體驗typescriptvue

第二課, 基礎類型和入門高級類型git

第三課, 泛型程序員

第四課, 解讀高級類型github

第五課, 命名空間(namespace)是什麼面試

插一課

原本打算接着上節課, 把高級類型都講完, 可是寫着寫着我發現高級類型中, 有不少地方都須要泛型的知識, 那麼先插一節泛型.typescript

什麼是"類型變量"和"泛型"

變量的概念咱們都知道, 能夠表示任意數據, 類型變量也同樣, 能夠表示任意類型:數組

// 在函數名後面用"<>"聲明一個類型變量
function convert<T>(input:T):T{
    return input;
}
複製代碼

convert中參數咱們標記爲類型T, 返回值也標記爲T, 從而表示了: 函數的輸入和輸出的類型一致. 這樣使用了"類型變量"的函數叫作泛型函數, 那有"泛型類"嗎?app

注意: T是我隨便定義的, 就和變量同樣, 名字你能夠隨便起, 只是建議都是大寫字母,好比U / RESULT.函數

泛型類
class Person<U> {
    who: U;
    
    constructor(who: U) {
        this.who = who;
    }

    say(code:U): string {
        return this.who + ' :i am ' + code;
    }
}
複製代碼

在類名後面經過"<>"聲明一個類型變量U, 類的方法和屬性均可以用這個U, 接下來咱們使用下泛型類:post

let a =  new Person<string>('詹姆斯邦德');
a.say(007) // 錯誤, 會提示參數應該是個string
a.say('007') // 正確
複製代碼

咱們傳入了類型變量(string),告訴ts這個類的Ustring類型, 經過Person的定義, 咱們知道say方法的參數也是string類型, 因此a.say(007)會報錯, 由於007是number. 多以咱們能夠經過傳入類型變量來約束泛型.

自動推斷類型變量的類型

其實咱們也能夠不指定類型變量爲string, 由於ts能夠根據實例化時傳入的參數的類型推斷出Ustring類型:

let a =  new Person('詹姆斯邦德');
// 等價 let a = new Person<string>('詹姆斯邦德');
a.say(007) // 錯誤, 會提示參數應該是個string
a.say('007') // 正確
複製代碼
泛型方法

其實方法和函數的定義方式同樣:

class ABC{
    // 輸入T[], 返回T
    getFirst<T>(data:T[]):T{
        return data[0];
    }
}
複製代碼
泛型是什麼?

說實話ts的文檔我找了好幾遍, 也沒看到他給泛型正式作定義, 只是表達他是一種描述多種類型(類型範圍)的格式, 我以爲有點抽象, 我用本身的理解具象下: 用動態的類型(類型變量)描述函數和類的方式.

泛型類型

咱們能夠用類型變量去描述一個類型(類型範圍), ts的數組類型Array自己就是一個泛型類型, 他須要傳遞具體的類型才能變的精準:

let arr : Array<number>;
arr = ['123']; // 錯誤, 提示數組中只能夠有number類型
arr = [123];
複製代碼

下面咱們本身定義一個泛型類型, 就對開頭的convert函數定義:

function convert<T>(input:T):T{
    return input;
}

// 定義泛型類型
interface Convert {
    <T>(input:T):T
}

// 驗證下
let convert2:Convert = convert // 正確不報錯
複製代碼

泛型接口

經過傳入不一樣的類型參數, 讓屬性更靈活:

interface Goods<T>{
    id:number;
    title: string;
    size: T;
}

let apple:Goods<string> = {id:1,title: '蘋果', size: 'large'};
let shoes:Goods<number> = {id:1,title: '蘋果', size: 43};
複製代碼

擴展類型變量(泛型約束)

function echo<T>(input: T): T {
    console.log(input.name); // 報錯, T上不肯定是否由name屬性
    return input;
}
複製代碼

前面說過T能夠表明任意類型, 但對應的都是基礎類型, 因此當咱們操做input.name的時候就須要標記input上有name屬性, 這樣就至關於咱們縮小了類型變量的範圍, 對泛型進行了約束:

// 如今T是個有name屬性的類型
function echo<T extends {name:string}>(input: T): T {
    console.log(input.name); // 正確
    return input;
}
複製代碼

一個泛型的應用, 工廠函數

function create<T,U>(O: {new(): T|U; }): T|U {
    return new O();
}
複製代碼

主要想說3個知識點:

  1. 能夠定義多個類型變量.
  2. 類型變量和普通類型用法一直, 也支持聯合類型/交叉類型等類型.
  3. 若是一個數據是能夠實例化的, 咱們能夠用{new(): any}表示.

不要亂用泛型

泛型主要是爲了約束, 或者說縮小類型範圍, 若是不能約束功能, 就表明不須要用泛型:

function convert<T>(input:T[]):number{
    return input.length;
}
複製代碼

這樣用泛型就沒有什麼意義了, 和any類型沒有什麼區別.

總結

泛型是編譯型語言最重要的特性, 泛型寫的好就會讓人以爲代碼很高級, 能夠說泛型是一個成手ts程序員必須熟練的技巧, 面試的時候是加分項, 因此你們寫代碼多多用泛型練習哦, 加油ヾ(◍°∇°◍)ノ゙,下面是的用ts寫的幾個小項目,寫的很差, 就是有份熱情, 拋磚引玉, 你們確定能寫出更好的:

手勢庫: github.com/any86/any-t…

命令式調用vue組件: github.com/any86/vue-c…

工做中經常使用的一些代碼片斷: github.com/any86/usefu…

一個mini的事件管理器: github.com/any86/any-e…

相關文章
相關標籤/搜索