国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

這篇具有很好參考價(jià)值的文章主要介紹了TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

前言

關(guān)于概念,本文不會(huì)過多敘述。

先來看個(gè)例子,體會(huì)一下泛型解決的問題吧。

我們定義一個(gè) print 函數(shù),這個(gè)函數(shù)的功能是把傳入的參數(shù)打印出來,最后再返回這個(gè)參數(shù),傳入?yún)?shù)的類型是 string,函數(shù)返回類型為 string

function print(arg: string): string {
    console.log(arg)
    return arg
}

假如現(xiàn)在需求變了,我還需要打印 number 類型,請(qǐng)問怎么辦?可使用聯(lián)合類型來改造!

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

現(xiàn)在需求又變了,我還需要打印 string 數(shù)組、number 數(shù)組,甚至任何類型,怎么辦?直接 any!

function print(arg:any):any {
    console.log(arg)
    return arg
}

需要注意的是寫 any 類型不好,畢竟在 TS 中盡量不要寫 any。

而且這也不是我們想要的結(jié)果,只能說傳入的值是 any 類型,輸出的值也是 any 類型,傳入和返回并不是統(tǒng)一的。

這么寫甚至還會(huì)出現(xiàn) bug

const res: string = print(123) 

定義 string 類型來接收 print 函數(shù)的返回值,返回的是個(gè) number 類型,TS 并不會(huì)報(bào)錯(cuò)提示我們。

這個(gè)時(shí)候,泛型就出現(xiàn)了,它可以輕松解決輸入輸出要一致的問題。

另外,泛型不是為了解決這一個(gè)問題設(shè)計(jì)出來的,泛型還解決了很多其他問題,這里是通過這個(gè)例子來引出泛型。

基本使用

泛型的語(yǔ)法是 <> 里寫類型參數(shù),一般可以用 T 來表示。

一、處理函數(shù)參數(shù)

我們使用泛型來解決前面的問題,如下代碼所示:

function print<T>(arg:T):T {
    console.log(arg)
    return arg
}

這樣,我們就做到了輸入和輸出的類型統(tǒng)一,且可以輸入輸出任何類型。

如果類型不統(tǒng)一,就會(huì)報(bào)錯(cuò):

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

泛型中的 T 就像一個(gè)占位符、或者說一個(gè)變量,在使用的時(shí)候可以把定義的類型 像參數(shù)一樣傳入,它可以 原封不動(dòng)地輸出。

泛型的寫法對(duì)前端工程師來說是有些古怪,比如 <> T ,但記住就好,只要一看到 <>,就知道這是泛型。


我們?cè)谑褂玫臅r(shí)候可以有兩種方式指定類型:

  1. 定義要使用的類型
  2. TS 類型推斷,自動(dòng)推導(dǎo)出類型
print<string>('hello')  // 定義 T 為 string
print('hello')  // TS 類型推斷,自動(dòng)推導(dǎo)類型為 string

我們知道,typeinterface 都可以定義函數(shù)類型,也用泛型來寫一下,type 這么寫:

type Print = <T>(arg: T) => T
const printFn:Print = function print(arg) {
    console.log(arg)
    return arg
}

interface 這么寫:

interface Iprint<T> {
    (arg: T): T
}

function print<T>(arg:T) {
    console.log(arg)
    return arg
}

const myPrint: Iprint<number> = print

二、默認(rèn)參數(shù)

如果要給泛型加默認(rèn)參數(shù),可以這么寫:

interface Iprint<T = number> {
    (arg: T): T
}

function print<T>(arg:T) {
    console.log(arg)
    return arg
}

const myPrint: Iprint = print

這樣默認(rèn)就是 number 類型了,怎么樣,是不是感覺 T 就如同函數(shù)參數(shù)一樣呢?

三、處理多個(gè)函數(shù)參數(shù)

現(xiàn)在有這么一個(gè)函數(shù),傳入一個(gè)只有兩項(xiàng)的元組,交換元組的第 0 項(xiàng)和第 1 項(xiàng),返回這個(gè)元組。

function swap(tuple) {
    return [tuple[1], tuple[0]]
}

這么寫,我們就喪失了類型,用泛型來改造一下。

我們用 T 代表第 0 項(xiàng)的類型,用 U 代表第 1 項(xiàng)的類型。

function swap<T, U>(tuple: [T, U]): [U, T]{
    return [tuple[1], tuple[0]]
}

這樣就可以實(shí)現(xiàn)了元組第 0 項(xiàng)和第 1 項(xiàng)類型的控制。

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

傳入的參數(shù)里,第 0 項(xiàng)為 string 類型,第 1 項(xiàng)為 number 類型。

在交換函數(shù)的返回值里,第 0 項(xiàng)為 number 類型,第 1 項(xiàng)為 string 類型。

第 0 項(xiàng)上全是 number 的方法。

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

第 1 項(xiàng)上全是 string 的方法。

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

四、函數(shù)副作用操作

泛型不僅可以很方便地約束函數(shù)的參數(shù)類型,還可以用在函數(shù)執(zhí)行副作用操作的時(shí)候。

比如我們有一個(gè)通用的異步請(qǐng)求方法,想根據(jù)不同的 url 請(qǐng)求返回不同類型的數(shù)據(jù)。

function request(url:string) {
    return fetch(url).then(res => res.json())
}

調(diào)一個(gè)獲取用戶信息的接口:

request('user/info').then(res =>{
    console.log(res)
})

這時(shí)候的返回結(jié)果 res 就是一個(gè) any 類型,非常討厭。

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

我們希望調(diào)用 API 都 清晰的知道返回類型是什么數(shù)據(jù)結(jié)構(gòu),就可以這么做:

interface UserInfo {
    name: string
    age: number
}

function request<T>(url:string): Promise<T> {
    return fetch(url).then(res => res.json())
}

request<UserInfo>('user/info').then(res =>{
    console.log(res)
})

這樣就能很舒服地拿到接口返回的數(shù)據(jù)類型,開發(fā)效率大大提高:

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

約束泛型

假設(shè)現(xiàn)在有這么一個(gè)函數(shù),打印傳入?yún)?shù)的長(zhǎng)度,我們這么寫:

function printLength<T>(arg: T): T {
    console.log(arg.length)
    return arg
}

因?yàn)椴淮_定 T 是否有 length 屬性,會(huì)報(bào)錯(cuò):

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

那么現(xiàn)在我想約束這個(gè)泛型,一定要有 length 屬性,怎么辦?

可以和 interface 結(jié)合,來約束類型。

interface ILength {
    length: number
}

function printLength<T extends ILength>(arg: T): T {
    console.log(arg.length)
    return arg
}

這其中的關(guān)鍵就是 <T extends ILength>,讓這個(gè)泛型繼承接口 ILength,這樣就能約束泛型。

我們定義的變量一定要有 length 屬性,比如下面的 str、arr 和 obj,才可以通過 TS 編譯。

const str = printLength('lin')
const arr = printLength([1,2,3])
const obj = printLength({ length: 10 })

這個(gè)例子也再次印證了 interface 的 duck typing。

只要你有 length 屬性,都符合約束,那就不管你是 str,arr 還是obj,都沒問題。

當(dāng)然,我們定義一個(gè)不包含 length 屬性的變量,比如數(shù)字,就會(huì)報(bào)錯(cuò):

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

泛型的一些應(yīng)用

使用泛型,可以在定義函數(shù)、接口或類的時(shí)候,不預(yù)先指定具體類型,而是在使用的時(shí)候再指定類型。

一、泛型約束類

定義一個(gè)棧,有入棧和出棧兩個(gè)方法,如果想入棧和出棧的元素類型統(tǒng)一,就可以這么寫:

class Stack<T> {
    private data: T[] = []
    push(item:T) {
        return this.data.push(item)
    }
    pop():T | undefined {
        return this.data.pop()
    }
}

在定義實(shí)例的時(shí)候?qū)戭愋?,比如,入棧和出棧都要?number 類型,就這么寫:

const s1 = new Stack<number>()

這樣,入棧一個(gè)字符串就會(huì)報(bào)錯(cuò):

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

這是非常靈活的,如果需求變了,入棧和出棧都要是 string 類型,在定義實(shí)例的時(shí)候改一下就好了:

const s1 = new Stack<string>()

這樣,入棧一個(gè)數(shù)字就會(huì)報(bào)錯(cuò):

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

特別注意的是,泛型無(wú)法約束類的靜態(tài)成員。

給 pop 方法定義 static 關(guān)鍵字,就報(bào)錯(cuò)了

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

二、泛型約束接口

使用泛型,也可以對(duì) interface 進(jìn)行改造,讓 interface 更靈活。

interface IKeyValue<T, U> {
    key: T
    value: U
}

const k1:IKeyValue<number, string> = { key: 18, value: 'lin'}
const k2:IKeyValue<string, number> = { key: 'lin', value: 18}

三、泛型定義數(shù)組

定義一個(gè)數(shù)組,我們之前是這么寫的:

const arr: number[] = [1,2,3]

現(xiàn)在這么寫也可以:

const arr: Array<number> = [1,2,3]

數(shù)組項(xiàng)寫錯(cuò)類型,報(bào)錯(cuò)

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

實(shí)戰(zhàn) - 泛型約束后端接口參數(shù)類型

我們來看一個(gè)泛型非常有助于項(xiàng)目開發(fā)的用法,約束后端接口參數(shù)類型。

import axios from 'axios'

interface API {
    '/book/detail': {
        id: number,
    },
    '/book/comment': {
        id: number
        comment: string
    }
    ...
}


function request<T extends keyof API>(url: T, obj: API[T]) {
    return axios.post(url, obj)
}

request('/book/comment', {
    id: 1,
    comment: '非常棒!'
})

這樣在調(diào)用接口的時(shí)候就會(huì)有提醒,比如:

路徑寫錯(cuò)了:

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

參數(shù)類型傳錯(cuò)了:

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

參數(shù)傳少了:

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)

寫在后面

泛型(Generics),從字面上理解,泛型就是一般的,廣泛的。

泛型是指在定義函數(shù)、接口或類的時(shí)候,不預(yù)先指定具體類型,而是在使用的時(shí)候再指定類型。


泛型中的 T 就像一個(gè)占位符、或者說一個(gè)變量,在使用的時(shí)候可以把定義的類型像參數(shù)一樣傳入,它可以原封不動(dòng)地輸出

泛型在成員之間提供有意義的約束,這些成員可以是:函數(shù)參數(shù)、函數(shù)返回值、類的實(shí)例成員、類的方法等。


用一張圖來總結(jié)一下泛型的好處:

TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)文章來源地址http://www.zghlxwxcb.cn/news/detail-413664.html

到了這里,關(guān)于TypeScript - 泛型 Generics(通俗易懂詳細(xì)教程)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包