TypeScript全解:泛型編程(上)

什么是泛型越妈?
泛籍嘹,指多
簡單來說就是多種類型

只要你能看懂 JS 的函數(shù),那么你就能看懂 TS 的泛型

JS:

const fn = (a, b) => a + b
const result = fn(1,2) // 3

TS:

type Fn<A, B> = A | B
type Result = Fn<string, number> // string | number

很像把瑟俭,格式上看起來一模一樣,非常簡單

思考:函數(shù)的本質(zhì)是什么契邀?

不知道大家平時(shí)在寫函數(shù)的時(shí)候摆寄,有沒有想過這個(gè)問題?

  • 復(fù)用代碼坯门?那我聲明就了一次微饥,也沒復(fù)用啊
  • 抽離邏輯?為了代碼更好看古戴?
  • 輸入輸出欠橘?這些目的是什么?不用函數(shù)我也能輸入輸出

個(gè)人認(rèn)為:函數(shù)的本質(zhì)是退后執(zhí)行的现恼、部分待定的代碼

console.log(1)

當(dāng)這部分代碼被解析的時(shí)候肃续, console 就已經(jīng)執(zhí)行了黍檩,那我們想晚一點(diǎn)執(zhí)行呢?用函數(shù)

const fn = () => console.log(1)
setTimeout(() => fn(), 2500)

所以函數(shù)的本質(zhì)之一就是:在我們這種面相過程的編程里面痹升,把執(zhí)行時(shí)機(jī)往后推遲

再繼續(xù)看建炫,還是這段代碼,這里的 fn 函數(shù)體非常死板疼蛾,只能打印出 1,于是我們改造:

const fn = (x) => console.log(x)

fn(222)
fn(333)

所以函數(shù)的第二個(gè)特點(diǎn)就是部分待定的代碼艺配,其實(shí)還是往后推遲察郁,這里的 console 我不想立馬確定,我希望能想什么時(shí)候執(zhí)行就什么時(shí)候執(zhí)行转唉,想 log 幾就 log 幾

舉一反三:泛型的本質(zhì)

泛型的本質(zhì)是退后執(zhí)行的皮钠、部分待定的類型

實(shí)例

function echo(n: number | string | boolean) {
  return n
}

實(shí)際上我們希望這個(gè)函數(shù)的類型是傳入什么類型就返回什么類型

  • echo(n: number) => number
  • echo(n: string) => string
  • echo(n: boolean) => boolean

很遺憾,如果不用泛型做不到赠法,即使你函數(shù)里面加了判斷

function echo(n: number | string | boolean) {
  if (typeof n === 'number'){
    return n
  } else if (typeof a === 'string') {
    return n
  } else {
    return n
  }
}

依然存在錯(cuò)亂的情況

沒有泛型麦轰,有些奇怪的需求就無法滿足
沒有泛型的類型系統(tǒng),就如同 JS 沒有函數(shù)

泛型-簡單難度

type Union<A, B> = A | B
type Intersect<A, B> = A & B

type List<T> = { // 就像函數(shù)一樣
  [key: string]: T
}

type Person = { name: string; age: number; }
List<Person> // 能匹配下面這些數(shù)據(jù) 
{
  a: Person,
  b: Person,
  ...
}

默認(rèn)值

既然函數(shù)可以有默認(rèn)值砖织,泛型能有么款侵?當(dāng)然有

type List<T = string> = {
  [key: string]: T
}

泛型-中等難度

來看一道題目

type Person = { name: string; }

type LikeString<T> = ??? // 如果這個(gè) T 是 string,則返回 true侧纯,否則 false
type LikeNumber<T> = ??? //如果這個(gè) T 是 number新锈,則返回 true,否則 false 
type LikePerson<T> = ??? // 如果這個(gè) T 是 Person眶熬,則返回 true妹笆,否則 false

type R1 = LikeString<'hi'> // true
type R2 = LikeString<true> // false
type S1 = LikeNumber<666> // 1
type S2 = LikeNumber<false> // 2
type T1 = LikePerson<{ name: 'hi', xxx: 1 }> // yes
type T2 = LikePerson<{ xxx: 1 }> // no

我們先想一下如果在 JS 中怎么實(shí)現(xiàn)

const likeString = a => typeof a === 'string' ? true : false

那么在 TS 中怎么實(shí)現(xiàn)呢?

type LikeString<T> = T extends string ? true : false

疑惑點(diǎn)來了娜氏,為何 TS 中的判斷要用 extends拳缠,為什么不能像 JS 那樣使用 ===,多有語義化

其實(shí)這跟類型兼容贸弥、父類型窟坐、子類型有關(guān)系,在類型中很多時(shí)候我們做不到完全一致茂腥,因?yàn)榧吓c集合之間相等的時(shí)候是很少的狸涌,大多時(shí)候是從屬關(guān)系,所以我們把這里的 extends 我更建議讀作包含于

剩下的實(shí)現(xiàn)就很簡單了:

type LikeNumber<T> = T extends number ? 1 : 2
type LikePerson<T> = T extends Person ? 'yes' : 'no'

其中會(huì)有這幾條規(guī)則

  1. 若 T 為 never最岗,則表達(dá)式的值為 never
  2. 若 T 為聯(lián)合類型帕胆,則分開計(jì)算

第一條規(guī)則:

type R1 = LikeString<never> // never

違背常理的事情又發(fā)生了,三元表達(dá)式竟然多計(jì)算出了一種結(jié)果般渡!竟然返回了 never懒豹!

第二條規(guī)則:

type ToArray<T> = T extends unknown ? T[] : never

type Result = toArray<string | number>
思考:此時(shí) Result 的類型是什么
  1. Result 為 (string | number)[]
  2. Result 為 string[] | number[]

答案是 2芙盘,為 string[] | number[],即為:

type Result = toArray<string | number>

type Result = string extends unknown ? string[] : never
               |
              number extends unknown ? number[] : never

為什么 TS 會(huì)這么推導(dǎo)呢脸秽?一開始我也很不理解儒老,直至后來我用函數(shù)來理解

偽代碼:

function fn(a: T): ToArray<T> {
  if (type a === 'string') {
    return string[] 
  } else if (typeof a === 'number') {
    return number[]
  }
}

實(shí)際上我們經(jīng)常會(huì)寫出這樣的函數(shù),根據(jù) a 的類型來判斷返回值的類型记餐,所以 TS 的預(yù)判是正確的

由此可推驮樊,上面返回的 never 也是合理的:

function fn(a: T): ToArray<T> {
  if (type a === 'string') {
    return string[] 
  } else if (typeof a === 'number') {
    return number[]
  } else {
    return never // TODO: 注意看這里,你傳了一個(gè) never 進(jìn)來片酝,自然而然就是返回 never
  }
}

注意上述兩條規(guī)則只對(duì)泛型有效(上面的代碼也一直在用函數(shù)的思路來舉例)

type X = never extends unknown ? 1 : 2 // 1

在加大一點(diǎn)難度

獲取對(duì)象的 key

type Person = { name: string; age: number; }

type GetKeys<T> = keyof T

type Result = GetKeys<Person> // name | age

判斷兩個(gè)類型是否相等

type Eq<A, B> = A extends B ? B extends A ? true : false : false

思路就是判斷 A 是否包含于 BB 是否包含于 A囚衔,那么為 true,否則為 false

很遺憾雕沿,不完全對(duì)练湿,看起來很正確,其實(shí)因?yàn)?分開計(jì)算 的規(guī)則下审轮,得到的并不是這樣的結(jié)果

獲取對(duì)象的 value

type Person = { name: string; age: number; }
type GetKeyType<T, K extends keyof T> = T[K]

type Result = GetKeyType<Person, 'name'> // string
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肥哎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疾渣,更是在濱河造成了極大的恐慌篡诽,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稳衬,死亡現(xiàn)場離奇詭異霞捡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)薄疚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門碧信,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人街夭,你說我怎么就攤上這事砰碴。” “怎么了板丽?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵呈枉,是天一觀的道長。 經(jīng)常有香客問我埃碱,道長猖辫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任砚殿,我火速辦了婚禮啃憎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘似炎。我一直安慰自己辛萍,他們只是感情好悯姊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贩毕,像睡著了一般悯许。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辉阶,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天先壕,我揣著相機(jī)與錄音,去河邊找鬼睛藻。 笑死启上,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的店印。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼倒慧,長吁一口氣:“原來是場噩夢啊……” “哼按摘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纫谅,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤炫贤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后付秕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兰珍,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年询吴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掠河。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猛计,死狀恐怖唠摹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奉瘤,我是刑警寧澤勾拉,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站盗温,受9級(jí)特大地震影響藕赞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜卖局,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一斧蜕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吼驶,春花似錦惩激、人聲如沸店煞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽顷蟀。三九已至,卻和暖如春骡技,著一層夾襖步出監(jiān)牢的瞬間鸣个,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工布朦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留囤萤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓是趴,卻偏偏與公主長得像涛舍,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唆途,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容