手寫十二個TypeScript內置工具類型

大家好败晴,我是愛水文的蘇先生房轿,一名從業(yè)5年+的前端愛好者粤攒,致力于用最通俗的文字分享前端知識的酸菜魚

github與好文

前言

TypeScript作為一個圖靈完備的類型系統(tǒng)冀续,可以幫助我們提高項目的可維護性并在開發(fā)階段就能幫助發(fā)現(xiàn)一些潛在的錯誤琼讽。時至今日,無論是我們平時工作中在項目里使用還是學習一些第三方的庫源碼洪唐,都能看到它的身影。因此學習它也已經(jīng)成為了一種必然

實際上吼蚁,當所有人都會而你不會的時候凭需,你便是那個被時代拋棄的人。你要非說前端最重要的是業(yè)務實現(xiàn)能力肝匆,是js粒蜈。那我要拿出"你js這么秀,為啥還學不會ts旗国?"枯怖,你又當如何應對?

APIS

Partial

  • 功能

將源類型中的key全部變成可選并返回一個新的類型

  • 實現(xiàn)

我們知道能曾,一個可選的屬性定義大致長這樣度硝,也就是說,我們只需要給屬性名后邊增加個?標記就可以了

interface Person{
    name?:string;
    age?:number;
}

對于Partial的接收參數(shù)來說寿冕,其擁有的key是不固定的蕊程,所以我們必須要想辦法拿到每一個key。在TypeScript中驼唱,keyof關鍵字可以將interface轉換成由鍵名組成的聯(lián)合類型藻茂。如下,我們拿到的新的聯(lián)合類型為T='name'|'age'

type T = keyof Person

現(xiàn)在我們只需要對聯(lián)合類型進行遍歷就能拿到每一個key了玫恳,在TypeScript中這對應in關鍵字辨赐。至此,我們只需要為每次拿到的key添加?標記就可以將原來的key變成可選了,最后再使用T[K]將對應的類型從Person中取出即可

type Partial<T> = {
   [K in keyof T]?: T[K];
};

Required

  • 功能

與Partial相反京办,將key變成必選掀序,并返回新的類型

  • 實現(xiàn)

這和Partial的實現(xiàn)思路一樣,我們只需要消掉?即可臂港,這在TypeScript中使用-來進行消除

type Partial<T> = {
   [K in keyof T]-?: T[K];
};

Readonly

  • 功能

將key變成只讀的森枪,并返回一個新的類型

  • 實現(xiàn)

這和Partial的實現(xiàn)思路也一樣视搏,我們只需要為其添加readonly關鍵字即可

type Partial<T> = {
   readonly [K in keyof T]: T[K];
};

Pick

  • 功能

挑選指定的key,并返回一個新的類型

  • 實現(xiàn)

想要從源類型中挑選指定的值县袱,首先我們需要將其作為參數(shù)傳遞給Pick浑娜,如下,我們通過泛形K來標識將來要挑選的值式散,至于泛形T筋遭,自然就是源類型了

type Pick<T,K>{}

比較好理解的是,我們的K不能是隨意的暴拄,它受到T的約束漓滔,這個我們使用extends來實現(xiàn),可以看到我們的K實際上是一個聯(lián)合類型

type Pick<T,K extends keyof T>{}

最后我們只需要遍歷這個聯(lián)合類型乖篷,并重新生成一個新的類型就可以了响驴,至于k的類型,和前邊一樣從源類型中提取即可

type Pick<T,K extends keyof T>{
    [P in K]:T[P]
}

Omit

  • 功能

從一個指定的對象類型中排除指定的key

  • 實現(xiàn)

首先撕蔼,它包含兩個泛形參數(shù)T和U豁鲤,T是源類型,U是要排除的key

type Omit<T,U> 

第二步鲸沮,我們來思考U是否需要約束琳骡,它應且本應只包含T中的key,傳遞不存在的實際上并不參與類型運算

type Omit<T,U extends keyof T>

第三步讼溺,我們拿到所有的key楣号,至于對應的類型,由于我們已經(jīng)約束了U怒坯,所以可以放心通過key從T中取即可

type Omit<T,U extends keyof T> = {
    [K in keyof T]:T[K]
}

最后就是對K進行類型判斷炫狱,如果它是U的子類型我們就排除,否則就保留

type Omit<T,U extends keyof T> = {
    [K in keyof T as P extends U ? never : P]:T[P]
}
  • 擴展解法

使用Pick和下文實現(xiàn)的Exclude也可以實現(xiàn)同樣的功能敬肚,有興趣的可以自己動手寫一下哦

Exclude

  • 功能

這個從功能上講和Omit有點像毕荐,不過它主要用于聯(lián)合類型。從第一個聯(lián)合類型參數(shù)中艳馒,將第二個聯(lián)合類型中出現(xiàn)的聯(lián)合項排除

  • 實現(xiàn)

首先我們確定了Exclude接收兩個參數(shù)

type Exclude<T,U>

我們在功能解釋中說明了這兩個泛形一般為聯(lián)合類型憎亚,那么是否需要對T和U進行約束呢?答案是不需要弄慰!因為尺有所短第美,寸有所長,它善于處理聯(lián)合類型不代表它只能處理聯(lián)合類型

那么我們如何做到和interface中那樣拿到每一個key呢陆爽?只有拿到了每一個key值我們才有機會去進行比較以確定排除什往。在TypeScript中,當使用extends對一個聯(lián)合類型執(zhí)行判斷時慌闭,它會拿每一個key來分別比較别威,故我們拿每一個T中的key判斷是否是U的子類型躯舔,是就設置為nerver標識排除就好了

type Exclude<T,U> = T extends U ? never : T

打call時間:

至此,十二個內置工具類型我們已經(jīng)學完一半了省古,我想你一定累了粥庄,所以我們休息一下

我目前正在開發(fā)一個名為unplugin-router的項目,它是一個約定式路由生成的庫,目前已支持在webpack和vite中使用豺妓,也已完成對vue-router3.x和vue-router4.x的支持惜互,且已經(jīng)接入到公司的一個vite3+vue3的項目中

不過受限于工作時間進度比較慢,在此尋找志同道合的朋友一起來完成這件事琳拭,后續(xù)計劃對功能做進一步的完善训堆,比如支持@hmr注解、支持權限路由等白嘁,也有對react路由和svelte路由的支持計劃坑鱼,以及除了webpack和vite這兩個之外的構建工具的支持,還有單元測試的編寫.....


Extract

  • 功能

從T類型中提取可以賦值給U的類型絮缅,返回一個聯(lián)合類型

  • 實現(xiàn)

同樣的它應有兩個泛形姑躲,不過不需要對其進行類型約束

type Extract<T,U>

前文我們已經(jīng)說過了,對于泛形是聯(lián)合類型時盟蚣,extends關鍵字會進行分配比較,所以我們相當于在拿T中的每一個key去判斷是否是U的子類型卖怜,不是就過濾掉

type Extract<T,U> = T extends U ? T : never

Record

  • 作用

構造具有類型T的一組屬性K的類型屎开,返回新的對象類型

  • 實現(xiàn)

同樣接受兩個泛形

type Record<T,U>

這里需要對泛形進行約束,對于泛形T马靠,必須為聯(lián)合類型

type Record<T extends keyof any,U>

然后只需要遍歷每一個key并為其設置類型為U即可

type Record<T,U> = {
    [k in T]:U
}

NonNullable

  • 功能

從給定的類型T中排除undefined和null

  • 實現(xiàn)

我們通過條件判斷是否是null或undefined奄抽,是就設置為never就好了

type NonNullable<T> = T extends null|undefined ? never : T
  • 擴展解法

通過空類型取交叉也可以達到同樣的效果,感興趣的可以自己嘗試下哦

Parameters

  • 功能

提取函數(shù)參數(shù)的類型

  • 實現(xiàn)

首先甩鳄,它接受一個函數(shù)的ts定義

type Parameters<T extends (...args:any)=>any>

接著逞度,我們對args進行推斷,這需要用到infer關鍵字

type Parameters<T extends (...args:any)=>any> T extends (...args:infer P)=>any ? P : never

ReturnType

  • 功能

獲取函數(shù)的返回值類型

  • 實現(xiàn)

和Parameters一樣妙啃,在返回值位置處進行推斷

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

ThisParameterType

  • 功能

獲取函數(shù)的this類型

  • 實現(xiàn)

和Parameters一樣档泽,同樣借助infer關鍵字在條件類型中推斷

type ThisParameterType<T extends (this:any,...args: any) => any> = T extends (this infer S,...args: any) => infer S ? S : any;
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市揖赴,隨后出現(xiàn)的幾起案子馆匿,更是在濱河造成了極大的恐慌,老刑警劉巖燥滑,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渐北,死亡現(xiàn)場離奇詭異,居然都是意外死亡铭拧,警方通過查閱死者的電腦和手機赃蛛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門恃锉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呕臂,你說我怎么就攤上這事破托。” “怎么了诵闭?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵炼团,是天一觀的道長。 經(jīng)常有香客問我疏尿,道長瘟芝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任褥琐,我火速辦了婚禮锌俱,結果婚禮上,老公的妹妹穿的比我還像新娘敌呈。我一直安慰自己贸宏,他們只是感情好,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布磕洪。 她就那樣靜靜地躺著吭练,像睡著了一般。 火紅的嫁衣襯著肌膚如雪析显。 梳的紋絲不亂的頭發(fā)上鲫咽,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音谷异,去河邊找鬼分尸。 笑死,一個胖子當著我的面吹牛歹嘹,可吹牛的內容都是我干的箩绍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼尺上,長吁一口氣:“原來是場噩夢啊……” “哼材蛛!你這毒婦竟也來了?” 一聲冷哼從身側響起尖昏,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤仰税,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后抽诉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陨簇,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了河绽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片己单。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖耙饰,靈堂內的尸體忽然破棺而出纹笼,到底是詐尸還是另有隱情,我是刑警寧澤苟跪,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布廷痘,位于F島的核電站,受9級特大地震影響件已,放射性物質發(fā)生泄漏笋额。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一篷扩、第九天 我趴在偏房一處隱蔽的房頂上張望兄猩。 院中可真熱鬧,春花似錦鉴未、人聲如沸枢冤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淹真。三九已至,卻和暖如春连茧,著一層夾襖步出監(jiān)牢的瞬間趟咆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工梅屉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鳞贷。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓坯汤,卻偏偏與公主長得像曹傀,于是被迫代替她去往敵國和親销斟。 傳聞我的和親對象是個殘疾皇子掉蔬,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容