前端深入理解Typescript泛型概念
首先介紹一下泛性的概念
泛型程序設(shè)計(jì)(generic programming)是程序設(shè)計(jì)語(yǔ)言的一種風(fēng)格或范式。泛型允許程序員在強(qiáng)類(lèi)型程序設(shè)計(jì)語(yǔ)言中編寫(xiě)代碼時(shí)使用一些以后才指定的類(lèi)型,在實(shí)例化時(shí)作為參數(shù)指明這些類(lèi)型。
泛型是指在定義函數(shù),接口或者類(lèi)的時(shí)候,不預(yù)先定義好具體的類(lèi)型,而在使用的時(shí)候在指定類(lèi)型的一種特性。
先舉一個(gè)簡(jiǎn)單的例子
假設(shè)我們定義一個(gè)函數(shù),它可以接收一個(gè)number類(lèi)型做為參數(shù),并且返回一個(gè)number類(lèi)型。
function genericDemo(data: number): number { return data; }
按照以上的寫(xiě)法是沒(méi)有問(wèn)題的,但是如果我們要接受一個(gè)string并返回一個(gè)string呢?如果邏輯一樣還要在寫(xiě)一遍嗎?就像下面這樣。
function genericDemo(data: string): string { return data; }
這顯然代碼是很冗余的,我們還有不使用any的寫(xiě)法嗎?答案是顯然易見(jiàn)的,可以使用范型的寫(xiě)法,就像下面這樣。
function genericDemo<T>(data: T):T { return data; }
我們?cè)诤瘮?shù)名稱(chēng)genericDemo后面聲明了范型變量<T>,他用于捕獲調(diào)用該函數(shù)時(shí)傳入的參數(shù)類(lèi)型(例如:number),之后我們就可以使用這個(gè)類(lèi)型。 之后我們?cè)俅问褂昧薚當(dāng)做返回值類(lèi)型?,F(xiàn)在我們可以知道參數(shù)類(lèi)型與返回值類(lèi)型是相同的了。這允許我們跟蹤函數(shù)里使用的類(lèi)型的信息。
多個(gè)類(lèi)型參數(shù)
我們?cè)诙x范型的時(shí)候,也可以一次定義多個(gè)類(lèi)型參數(shù),像下面這樣。
function swap<T, U>(tuple: [T, U]):[U, T] { return [tuple[1], tuple[0]]; }
泛型接口
我們先定義一個(gè)范型接口Identities,然后定義一個(gè)函數(shù)identities()來(lái)使用這個(gè)范型接口
interface Identities<T, U> { id1: T; id2: U; }
我在這里使用T和U作為我們的類(lèi)型變量來(lái)演示任何字母(或有效的字母數(shù)字名稱(chēng)的組合)都是有效的類(lèi)型—除了常規(guī)用途之外,您對(duì)它們的調(diào)用沒(méi)有任何意義。
我們現(xiàn)在可以將這個(gè)接口應(yīng)用為identity()的返回類(lèi)型,修改我們的返回類(lèi)型以符合它。我們還可以console.log這些參數(shù)和它們的類(lèi)型,以便進(jìn)一步說(shuō)明:
function identities<T, U> (arg1: T, arg2: U): Identities<T, U> { console.log(arg1 + ": " + typeof (arg1)); console.log(arg2 + ": " + typeof (arg2)); let identities: Identities<T, U> = { id1: arg1, id2: arg2 }; return identities; }
我們現(xiàn)在對(duì)identity()所做的是將類(lèi)型T和U傳遞到函數(shù)和identity接口中,從而允許我們定義與參數(shù)類(lèi)型相關(guān)的返回類(lèi)型。
范型變量
使用泛型創(chuàng)建像identity這樣的泛型函數(shù)時(shí),編譯器要求你在函數(shù)體必須正確的使用這個(gè)通用的類(lèi)型。 換句話(huà)說(shuō),你必須把這些參數(shù)當(dāng)做是任意或所有類(lèi)型。
我們先看下之前例子
function genericDemo<T>(data: T):T { return data; }
如果我們想同時(shí)打印出data的長(zhǎng)度。 我們很可能會(huì)這樣做
function genericDemo<T>(data: T):T { console.log(data.length); // Error: T doesn't have .length return data; }
如果這么做,編譯器會(huì)報(bào)錯(cuò)說(shuō)我們使用了data的.length屬性,但是沒(méi)有地方指明data具有這個(gè)屬性。 記住,這些類(lèi)型變量代表的是任意類(lèi)型,所以使用這個(gè)函數(shù)的人可能傳入的是個(gè)數(shù)字,而數(shù)字是沒(méi)有.length屬性的。
現(xiàn)在假設(shè)我們想操作T類(lèi)型的數(shù)組而不直接是T。由于我們操作的是數(shù)組,所以.length屬性是應(yīng)該存在的。 我們可以像創(chuàng)建其它數(shù)組一樣創(chuàng)建這個(gè)數(shù)組:
function genericDemo<T>(data: Array<T>):Array<T> { console.log(data.length); return data; }
范型類(lèi)
我們還可以在類(lèi)屬性和方法的意義上使類(lèi)泛型。泛型類(lèi)確保在整個(gè)類(lèi)中一致地使用指定的數(shù)據(jù)類(lèi)型。例如下面這種在React Typescript項(xiàng)目中的寫(xiě)法。
interface Props { className?: string; ... } interface State { submitted?: bool; ... } class MyComponent extends React.Component<Props, State> { ... }
我們?cè)谶@里使用與React組件一起使用的泛型,以確保組件的props和state是類(lèi)型安全的。
泛型約束
我們先看一個(gè)常見(jiàn)的需求,我們要設(shè)計(jì)一個(gè)函數(shù),這個(gè)函數(shù)接受兩個(gè)參數(shù),一個(gè)參數(shù)為對(duì)象,另一個(gè)參數(shù)為對(duì)象上的屬性,我們通過(guò)這兩個(gè)參數(shù)返回這個(gè)屬性的值,比如:
function getValue(obj: object, key: string){ return obj[key] // error }
我們會(huì)得到一段報(bào)錯(cuò),這是新手 TypeScript 開(kāi)發(fā)者常常犯的錯(cuò)誤,編譯器告訴我們,參數(shù) obj 實(shí)際上是 {},因此后面的 key 是無(wú)法在上面取到任何值的。
因?yàn)槲覀兘o參數(shù) obj 定義的類(lèi)型就是 object,在默認(rèn)情況下它只能是 {},但是我們接受的對(duì)象是各種各樣的,我們需要一個(gè)泛型來(lái)表示傳入的對(duì)象類(lèi)型,比如T extends object:
function getValue<T extends object>(obj: T, key: string) { return obj[key] // error }
這依然解決不了問(wèn)題,因?yàn)槲覀兊诙€(gè)參數(shù) key 是不是存在于 obj 上是無(wú)法確定的,因此我們需要對(duì)這個(gè) key 也進(jìn)行約束,我們把它約束為只存在于 obj 屬性的類(lèi)型,這個(gè)時(shí)候需要借助到后面我們會(huì)進(jìn)行學(xué)習(xí)的索引類(lèi)型進(jìn)行實(shí)現(xiàn) <U extends keyof T>,我們用索引類(lèi)型 keyof T 把傳入的對(duì)象的屬性類(lèi)型取出生成一個(gè)聯(lián)合類(lèi)型,這里的泛型 U 被約束在這個(gè)聯(lián)合類(lèi)型中,這樣一來(lái)函數(shù)就被完整定義了:
function getValue<T extends object, U extends keyof T>(obj: T, key: U) { return obj[key] // ok }
另外提一個(gè)多重泛型約束的寫(xiě)法,可以當(dāng)作拓展:
interface firstInterface { first(): number } interface secondInterface { second(): string } class Demo<T extends firstInterface & secondInterface >{ ... }
在泛型里使用類(lèi)類(lèi)型
在TypeScript使用泛型創(chuàng)建工廠函數(shù)時(shí),需要引用構(gòu)造函數(shù)的類(lèi)類(lèi)型。比如:
function create<T>(type: {new(): T; }): T { return new type(); }
參數(shù)type的類(lèi)型{new(): T}就表示此泛型T是可被構(gòu)造的,在被實(shí)例化后的類(lèi)型是泛型 T。
總結(jié)
到此這篇關(guān)于前端深入理解Typescript泛型概念的文章就介紹到這了,更多相關(guān)Typescript 泛型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript計(jì)算出現(xiàn)精度丟失問(wèn)題的解決方法
Javascript作為一門(mén)大型編程語(yǔ)言,在日常開(kāi)發(fā)中難免會(huì)涉及到大量的數(shù)學(xué)計(jì)算,然而,浮點(diǎn)數(shù)在計(jì)算過(guò)程中可能出現(xiàn)精度的問(wèn)題,下面我們就來(lái)學(xué)習(xí)一下Javascript中高精度計(jì)算及其相關(guān)知識(shí)吧2023-11-11webpack中CommonsChunkPlugin詳細(xì)教程(小結(jié))
本篇文章主要介紹了webpack中CommonsChunkPlugin詳細(xì)教程(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11微信小程序?qū)崿F(xiàn)簡(jiǎn)單的select下拉框
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)簡(jiǎn)單的select下拉框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11JavaScript 創(chuàng)建隨機(jī)數(shù)和隨機(jī)圖片
關(guān)于javascript隨機(jī)數(shù)的,很早以前的文章了,不過(guò)內(nèi)容還是不錯(cuò)的,如果想要更多的效果,可以去腳本之家搜下。2009-12-12TypeScript利用TS封裝Axios實(shí)戰(zhàn)
這篇文章主要介紹了TypeScript利用TS封裝Axios實(shí)戰(zhàn),TypeScript封裝一遍Axios,能進(jìn)一步鞏固TypeScript的基礎(chǔ)知識(shí),需要的小伙伴可以參考一下2022-06-06微信小程序日期增加時(shí)間完成訂單失效倒計(jì)時(shí)效果
這篇文章主要介紹了微信小程序日期增加時(shí)間完成訂單失效倒計(jì)時(shí)效果,在我們?nèi)粘Y?gòu)物過(guò)程中經(jīng)常會(huì)遇到這樣的功能,本文通過(guò)示例代碼給大家詳細(xì)講解,需要的朋友參考下吧2024-04-04JavaScript實(shí)現(xiàn)水印效果的示例代碼
這篇文章主要為大家詳細(xì)介紹了JavaScript如何利用canvas實(shí)現(xiàn)添加水印的效果,文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-05-05javascript學(xué)習(xí)隨筆(使用window和frame)的技巧
javascript學(xué)習(xí)隨筆(使用window和frame)的技巧...2007-03-03