ts 類型體操之內(nèi)置工具類型(中)

書接上文琳彩。在上一節(jié)中,我們學(xué)習(xí)了 8 個最常用的內(nèi)置工具類型碧浊。這些工具類型都是對現(xiàn)有類型進行“變形”的工具瘟仿,它們可以改變類型的結(jié)構(gòu)劳较,但不會改變類型本身的值。這些內(nèi)置工具類型臊恋,本質(zhì)上就是類型系統(tǒng)中的“函數(shù)”墓捻,它們接受范型作為參數(shù)砖第,返回一個新的類型。

這一節(jié)放吩,我們繼續(xù)學(xué)習(xí)內(nèi)置工具類型羽杰,主要集中學(xué)習(xí)infer相關(guān)的幾個內(nèi)置工具:

Parameters<T>:獲取函數(shù)類型 T 的返回參數(shù)列表

如下例所示忽洛,以元祖形式返回函數(shù)參數(shù)列表:

type T1 = Parameters<(s: string, n: string) => void>; // [string, number]

實現(xiàn)用到了我們基礎(chǔ)語法篇里提到的infer;在這里,我們使用 infer P 來推斷函數(shù)參數(shù)的類型复哆,并將其賦值給 P腌零。

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

簡單介紹一下:

  1. 所有函數(shù)類型的基類是 (...args: any) => any益涧;Parameters 用于函數(shù)類型的參數(shù)類型提取,所以我們給范型(參數(shù))T加一個限制 T extends (...args: any) => any久免,確保 T 是函數(shù)類型,否則類型拋錯
  2. 類型推斷 infer 只能配合條件判斷 extends 使用记舆;所以我們需要冗余地寫一遍類似的代碼 T extends (...args: infer P) => any ? P : never泽腮;這次主要是為了推斷出參數(shù) args 的類型 P衣赶,然后返回該類型。

p.s. 上面實現(xiàn)中兩個 extends 作用不同:第一個是用于類型限制豪筝;第二個是配合infer的條件判斷摘能。大家不要搞混了团搞。

ReturnType<T>:獲取函數(shù)類型 T 的返回類型

同理ReturnType的實現(xiàn)也和Parameters差不多,只不過把推斷的參數(shù)類型換成了返回類型:

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

我們擴展一下像吻,能不能自己實現(xiàn)一個類型工具复隆,同時返回參數(shù)類型和返回類型呢挽拂?當(dāng)然可以,這就是兩個infer推斷的事台腥,如下所示:

type ParametersAndReturnType<T extends (...args: any) => any> = T extends (
  ...args: infer P
) => infer R
  ? { parameters: P; return: R }
  : any;

type T2 = ParametersAndReturnType<(a: string) => number>;

// type T2 = {
//     parameters: [a: string];
//     return: number;
// }

ConstructorParameters<T>:獲取構(gòu)造函數(shù)類型 T 的參數(shù)類型

上文提到:所有函數(shù)類型的基類型是 (...args: any) => any黎侈。而所有構(gòu)造函數(shù)類型 T 的基類型是:

new (...args: any) => any

由于js的類(class)事實上是構(gòu)造函數(shù)的一個語法糖闷游,所以我們還需要考慮class。普通class的基類也自然是new (...args: any) => any扳埂。但是蛛碌,ts多走了一步,支持了抽象類(abstract class)希太,所以又給new (...args: any) => any找了個基類——abstract new (...args: any) => any誊辉。最終亡脑,我們判斷T是否是構(gòu)造函數(shù),就成了判斷T是否是abstract new (...args: any) => any的子類蛙紫。

ConstructorParameters就是提取構(gòu)造函數(shù)的參數(shù)類型坑傅,實現(xiàn)上和Parameters<T>差不多——一個infer的事:

type ConstructorParameters<T extends abstract new (...args: any) => any> =
  T extends abstract new (...args: infer P) => any ? P : never;

InstanceType<T>:獲取構(gòu)造函數(shù)類型 T 的實例類型

InstanceType就是獲取構(gòu)造函數(shù)的返回類型唁毒,實現(xiàn)上參考ReturnType<T>星爪,也很簡單:

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

其他

除了上面提到的幾個,typescript 3.3 內(nèi)置了ThisParameterType<T>近零、OmitThisParameter<T>秒赤、ThisType<T>憎瘸。這些工具其實為了兼容typescript 2.0 版本里的 this 聲明展開的:類型體操中完全用不到幌甘,現(xiàn)實開發(fā)中也應(yīng)盡量避免在函數(shù)里用 this。這里由于與infer相關(guān)酥诽,我也把它們的實現(xiàn)列一下:

  • ThisParameterType<T>:提取函數(shù)類型的this參數(shù)的類型皱埠,如果函數(shù)類型沒有this參數(shù)边器,則返回unknown

    type ThisParameterType<T> = T extends (this: infer U, ...args: never) => any
      ? U
      : unknown;
    

    實現(xiàn)倒不難,就是調(diào)用的時候有點蠢:要在函數(shù)的第一個參數(shù)里聲明this類型恒界,而且還不能簡單調(diào)用該函數(shù)十酣,要配合apply际长、call工育、bind使用。

    function foo(this: string) {
      return this + ':Hello world';
    }
    
    // type of foo => (this: string) => string
    
    type Foo = ThisParameterType<typeof foo>; // string
    
    function numberToString(s: Foo) {
      return foo.call(s);
    }
    
  • OmitThisParameter<T>:移除函數(shù)類型的this參數(shù)

    type OmitThisParameter<T> =
      unknown extends ThisParameterType<T>
        ? T
        : T extends (...args: infer A) => infer R
          ? (...args: A) => R
          : T;
    
    
      type omitThis = OmitThisParameter<(this: number, n: number) => void> // (n: number) => void
    

    這個就是移除了this聲明的函數(shù)類型文留,稍微解釋一下:

    1. unknown extends ThisParameterType<T>: 結(jié)合 ThisParameterType 的實現(xiàn)燥翅,我們可以得出蜕提,如果函數(shù)類型沒有this聲明谎势,那么ThisParameterType<T> 直接返回unknown;所以這里就是單純判斷T有沒有this聲明猖毫,如果沒有须喂,直接返回T本身。
    2. T extends (...args: infer A) => infer R: 如果T是函數(shù)類型掷伙,則提取參數(shù)類型A和返回值類型R又兵,反之直接返回類型T本身沛厨。p.s. 這種條件寫法與 T extends (this: infer U , ...args: infer A) => infer R 區(qū)別是:會自動忽略this參數(shù)宙地。
    3. (...args: A) => R: 返回一個新的函數(shù)類型,這個函數(shù)類型不再聲明this類型俄烁,其他參數(shù)類型和返回值類型與T相同绸栅。

    實現(xiàn)上也挺簡單,就是有兩層條件判斷页屠。以后我們接觸type challenge真題時粹胯,會碰到更多層的情況。不要慌辰企,可以把代碼類似“抽取函數(shù)”(類型嵌套)的形式來重構(gòu)风纠。比如:

    type OmitThisParameter<T> =
      unknown extends ThisParameterType<T> ? T : OmitThisParameterFunc<T>;
    
    type OmitThisParameterFunc<T> = T extends (...args: infer A) => infer R
      ? (...args: A) => R
      : T;
    
  • ThisType<T>:非推理類型位置的標(biāo)記

    早古的設(shè)計:沒啥用,就是個this的標(biāo)記位牢贸,等于空接口,一筆帶過了潜索。

    interface ThisType<T> = {};
    

小結(jié)

這期我們主要講了infer相關(guān)的幾個內(nèi)置工具類型臭增。infer是type challenge中除了extends以外出場頻率最高的一個關(guān)鍵字,它能夠讓我們在類型體操中實現(xiàn)很多看似不可能的功能竹习。我想大家在學(xué)習(xí)過這幾個工具后誊抛,應(yīng)該能對infer有更深的理解。

本文是該系列的第四篇文章整陌,我們回過頭來思考一下type challenge(類型體操)拗窃,解決這類問題到底有多少意義?其實它就是類似于leetcode的代碼訓(xùn)練泌辫。很多人對leetcode嗤之以鼻(我個人還是比較肯定leetcode作為日常代碼訓(xùn)練的意義的随夸,堅持每天一道leetcode),但是在面試中震放,leetcode題目還是占據(jù)著舉足輕重的地位宾毒。類型體操自然沒有達到leetcode這種業(yè)界地位,b不過一到涉及typescript的高級特性考察殿遂,你覺得面試官能問什么問題呢伍俘?我們學(xué)習(xí)一種語言本質(zhì)上是對自己職業(yè)生涯的一項投資邪锌,投資的最大回報就是找到下一份滿意的工作勉躺。即然你已經(jīng)決定學(xué)習(xí)typescript癌瘾,那就務(wù)必掌握好它最核心的部分——類型系統(tǒng)。

文章同步發(fā)布于an-Onion 的 Github饵溅。碼字不易妨退,歡迎點贊。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(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
  • 正文 為了忘掉前任,我火速辦了婚禮氏堤,結(jié)果婚禮上沙绝,老公的妹妹穿的比我還像新娘。我一直安慰自己鼠锈,他們只是感情好闪檬,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著购笆,像睡著了一般粗悯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上同欠,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天样傍,我揣著相機與錄音横缔,去河邊找鬼。 笑死衫哥,一個胖子當(dāng)著我的面吹牛茎刚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撤逢,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼膛锭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚊荣?” 一聲冷哼從身側(cè)響起初狰,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎互例,沒想到半個月后奢入,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡媳叨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年腥光,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肩杈。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡柴我,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扩然,到底是詐尸還是另有隱情艘儒,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布夫偶,位于F島的核電站界睁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏兵拢。R本人自食惡果不足惜翻斟,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望说铃。 院中可真熱鬧访惜,春花似錦、人聲如沸腻扇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幼苛。三九已至窒篱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背墙杯。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工配并, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人高镐。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓溉旋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親避消。 傳聞我的和親對象是個殘疾皇子低滩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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