第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

TypeScript 泛型(Generic)

本節(jié)開始介紹 TypeScript 一些進(jìn)階知識(shí)點(diǎn),第一個(gè)要介紹的泛型是 TypeScript 中非常重要的一個(gè)概念,它是一種用以增強(qiáng)函數(shù)、類和接口能力的非常可靠的手段。

使用泛型,我們可以輕松地將那些輸入重復(fù)的代碼,構(gòu)建為可復(fù)用的組件,這給予了開發(fā)者創(chuàng)造靈活、可重用代碼的能力。

1. 慕課解釋

泛型在傳統(tǒng)的面向?qū)ο笳Z言中極為常見,可以使用泛型來創(chuàng)建可重用的組件,一個(gè)組件可以支持多種類型的數(shù)據(jù)。

通俗來講:泛型是指在定義函數(shù)、接口或者類時(shí),未指定其參數(shù)類型,只有在運(yùn)行時(shí)傳入才能確定。那么此時(shí)的參數(shù)類型就是一個(gè)變量,通常用大寫字母 T 來表示,當(dāng)然你也可以使用其他字符,如:UK等。

語法:在函數(shù)名、接口名或者類名添加后綴 <T>

function generic<T>() {}
interface Generic<T> {}
class Generic<T> {}

2. 初識(shí)泛型

之所以使用泛型,是因?yàn)樗鼛椭覀優(yōu)椴煌愋偷妮斎耄瑥?fù)用相同的代碼。

比如寫一個(gè)最簡單的函數(shù),這個(gè)函數(shù)會(huì)返回任何傳入它的值。如果傳入的是 number 類型:

function identity(arg: number): number {
    return arg
}

如果傳入的是 string 類型:

function identity(arg: string): string {
    return arg
}

通過泛型,可以把兩個(gè)函數(shù)統(tǒng)一起來:

function identity<T>(arg: T): T {
  return arg
}

需要注意的是,泛型函數(shù)的返回值類型是根據(jù)你的業(yè)務(wù)需求決定,并非一定要返回泛型類型 T:

function identity<T>(arg: T): string {
  return String(arg)
}

代碼解釋: 入?yún)⒌念愋褪俏粗?,但是通過 String 轉(zhuǎn)換,返回字符串類型。

3. 多個(gè)類型參數(shù)

泛型函數(shù)可以定義多個(gè)類型參數(shù):

function extend<T, U>(first: T, second: U): T & U {
  for(const key in second) {
    (first as T & U)[key] = second[key] as any
  }
  return first as T & U
}

代碼解釋: 這個(gè)函數(shù)用來合并兩個(gè)對(duì)象,具體實(shí)現(xiàn)暫且不去管它,這里只需要關(guān)注泛型多個(gè)類型參數(shù)的使用方式,其語法為通過逗號(hào)分隔 <T, U, K>。

4. 泛型參數(shù)默認(rèn)類型

函數(shù)參數(shù)可以定義默認(rèn)值,泛型參數(shù)同樣可以定義默認(rèn)類型:

實(shí)例演示
預(yù)覽 復(fù)制
復(fù)制成功!
function min<T = number>(arr:T[]): T{
  let min = arr[0]
  arr.forEach((value)=>{
     if(value < min) {
         min = value
     }
  })
   return min
}
console.log(min([20, 6, 8n])) // 6
運(yùn)行案例 點(diǎn)擊 "運(yùn)行案例" 可查看在線運(yùn)行效果

解釋: 同樣的不用去關(guān)注這個(gè)最小數(shù)函數(shù)的具體實(shí)現(xiàn),要知道默認(rèn)參數(shù)語法為 <T = 默認(rèn)類型>。

5. 泛型類型與泛型接口

先來回顧下之前章節(jié)介紹的函數(shù)類型:

const add: (x: number, y: number) => string = function(x: number, y: number): string {
  return (x + y).toString()
}

等號(hào)左側(cè)的 (x: number, y: number) => string 為函數(shù)類型。

再看下泛型類型:

function identity<T>(arg: T): T {
  return arg
}

let myIdentity: <T>(arg: T) => T = identity

同樣的等號(hào)左側(cè)的 <T>(arg: T) => T 即為泛型類型,它還有另一種帶有調(diào)用簽名的對(duì)象字面量書寫方式:{ <T>(arg: T): T }:

function identity<T>(arg: T): T {
  return arg
}

let myIdentity: { <T>(arg: T): T } = identity

這就引導(dǎo)我們?nèi)懙谝粋€(gè)泛型接口了。把上面例子里的對(duì)象字面量拿出來作為一個(gè)接口:

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

function identity<T>(arg: T): T {
  return arg
}

let myIdentity: GenericIdentityFn = identity

進(jìn)一步,把泛型參數(shù)當(dāng)作整個(gè)接口的一個(gè)參數(shù),我們可以把泛型參數(shù)提前到接口名上。這樣我們就能清楚的知道使用的具體是哪個(gè)泛型類型:

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

function identity<T>(arg: T): T {
  return arg
}

let myIdentity: GenericIdentityFn<number> = identity

注意,在使用泛型接口時(shí),需要傳入一個(gè)類型參數(shù)來指定泛型類型。示例中傳入了 number 類型,這就鎖定了之后代碼里使用的類型。

6. 泛型類

始終要記得,使用泛型是因?yàn)榭梢詮?fù)用不同類型的代碼。下面用一個(gè)最小堆算法舉例說明泛型類的使用:

class MinClass {
  public list: number[] = []
  add(num: number) {
    this.list.push(num)
  }
  min(): number {
    let minNum = this.list[0]
    for (let i = 0; i < this.list.length; i++) {
      if (minNum > this.list[i]) {
        minNum = this.list[i]
      }
    }
    return minNum
  }
}

代碼解釋: 示例中我們實(shí)現(xiàn)了一個(gè)查找 number 類型的最小堆類,但我們的最小堆還需要支持字符串類型,此時(shí)就需要泛型的幫助了:

實(shí)例演示
預(yù)覽 復(fù)制
復(fù)制成功!
// 類名后加上 <T>
class MinClass<T> {
  public list: T[] = []
  add(num: T) {
    this.list.push(num)
  }
  min(): T {
    let minNum = this.list[0]
    for (let i = 0; i < this.list.length; i++) {
      if (minNum > this.list[i]) {
        minNum = this.list[i]
      }
    }
    return minNum
  }
}


let m = new MinClass<string>()
m.add('hello')
m.add('world')
m.add('generic')
console.log(m.min()) // generic
運(yùn)行案例 點(diǎn)擊 "運(yùn)行案例" 可查看在線運(yùn)行效果

代碼解釋:

第 2 行,在聲明 類 MinClass 的后面后加上了 <T>,這樣就聲明了泛型參數(shù) T,作為一個(gè)變量可以是字符串類型,也可以是數(shù)字類型。

7. 泛型約束

語法:通過 extends 關(guān)鍵字來實(shí)現(xiàn)泛型約束。

如果我們很明確傳入的泛型參數(shù)是什么類型,或者明確想要操作的某類型的值具有什么屬性,那么就需要對(duì)泛型進(jìn)行約束。通過兩個(gè)例子來說明:

interface User {
  username: string
}

function info<T extends User>(user: T): string {
  return 'imooc ' + user.username
}

代碼解釋: 示例中,第 5 行,我們約束了入?yún)?user 必須包含 username 屬性,否則在編譯階段就會(huì)報(bào)錯(cuò)。

下面再看另外一個(gè)例子:

type Args = number | string

class MinClass<T extends Args> {}

const m = new MinClass<boolean>() // Error, 必須是 number | string 類型

代碼解釋:

第 3 行,約束了泛型參數(shù) T 繼承自類型 Args,而類型 Args 是一個(gè)由 number 和 string 組成的聯(lián)合類型。

第 5 行,泛型參數(shù)只能是 number 和 string 中的一種,傳入 boolean 類型是錯(cuò)誤的。

8. 多重類型泛型約束

通過 <T extends Interface1 & Interface2> 這種語法來實(shí)現(xiàn)多重類型的泛型約束:

interface Sentence {
  title: string,
  content: string
}

interface Music {
  url: string
}

class Classic<T extends Sentence & Music> {
  private prop: T

  constructor(arg: T) {
    this.prop = arg
  }

  info() {
    return {
      url: this.prop.url,
      title: this.prop.title,
      content: this.prop.content
    }
  }
}

代碼解釋:

第 10 行,約束了泛型參數(shù) T 需繼承自交叉類型(后續(xù)有單節(jié)介紹) Sentence & Music,這樣就能訪問兩個(gè)接口類型的參數(shù)。

9. 小結(jié)

泛型在 TypeScript 中用途廣泛,可以靈活的控制類型之間的約束,提高代碼復(fù)用性,增強(qiáng)代碼可讀性。