<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          TypeScript:一個好泛型的價值

          共 4322字,需瀏覽 9分鐘

           ·

          2021-01-14 23:05

          原文轉(zhuǎn)自:https://juejin.im/post/6878868818836488205

          TypeScript:一個好泛型的價值

          在軟件開發(fā)領(lǐng)域,我們總是致力于創(chuàng)建可復(fù)用的組件,架構(gòu)被設(shè)計為可適應(yīng)多種情境,并且我們始終在尋找一種即便在面臨未知情況時,也能自動讓邏輯正確行事的方法。

          盡管在某些情境下可能并不總是易于做到甚或根本不可行,但我們心里總想找到能被復(fù)現(xiàn)并變?yōu)榭杀粦?yīng)付的通用算法的某些模式。所謂?泛型(Generics)?的概念就是該行為的另一個例子,只是,這次我們不訴諸宏大,而是在代碼層面的細(xì)枝末節(jié)中試圖找出并描繪上述的模式。

          且聽我細(xì)細(xì)道來……

          何為泛型?

          泛型是種一旦理解就樂在其中的概念,所以讓我只是先從這樣描述它開始吧:

          泛型之于類型(Types),猶類型之于變量也

          換言之,泛型為你提供了一種不用指定特別某種類型就能使用若干類型的方式。這給你的函數(shù)定義、類型定義,甚至接口定義賦予了更高一層的靈活性。

          用于解釋泛型威力的典型例子,莫過于 identity 函數(shù)。該函數(shù)本質(zhì)上只是原樣返回你傳入的唯一參數(shù),別無他用,但如果你思考一下,如何在一種強(qiáng)類型語言中定義這樣一個函數(shù)呢?

          function?identity(value:?number):number?{
          ??return?value;
          }

          上面的函數(shù)對于數(shù)字工作良好,那字符串呢?或布爾值?自定義類型又如何?在 TypeScript 中要覆蓋所有可能性,明顯只能選擇?any?類型了:

          function?identity(value:?any):?any?{
          ??return?value
          }

          這還挺行得通的,但此刻你的函數(shù)實際上丟失了所有類型的概念,你將不能在本該有確定類型信息的地方使用它們了。本質(zhì)上來說現(xiàn)在你可以傳入任何值而編譯器將一言不發(fā),這和你使用普通的 JavaScript 就沒有區(qū)別了(即無論怎樣都沒有類型信息了):

          let?myVar?=?identity("Fernando")

          console.log(myVar.length)?//?工作良好!

          myVar?=?identity(23)

          console.log(myVar.length)?//?也能工作,盡管打印了?"undefined"

          現(xiàn)在因為沒有類型信息了,編譯器就不能檢查和函數(shù)相關(guān)的任何事情以及變量了,如此一來我們運(yùn)行出了一個意外的 “undefined”(若將本例推及有更多邏輯復(fù)雜邏輯的真實場景將很可能變成最糟糕的一段程序)。

          我們該如何修復(fù)這點并避免使用?any?類型呢?

          TypeScript 泛型來拯救

          正如我曾 嘗試 說的那樣:一個泛型就像若干類型的一個變量,這意味著我們可以定義一個表示任何類型的變量,同時能保持住類型信息。后者是關(guān)鍵,因為那正是?any?做不到的。基于這種想法,現(xiàn)在可以這樣重構(gòu)我們的 identity 函數(shù):

          function?identity<T>(value:?T):?T?{
          ??return?value;
          }

          記住,用來表示泛型名字的可以是任意字母,你可以隨意命名。但使用一個單字母呢,看起來是個標(biāo)準(zhǔn)了,所以我們也從善如流。

          這不單讓我們定義了一個可被任意類型使用的函數(shù),現(xiàn)在相關(guān)的變量也將保留你所選擇類型的正確信息。如下:

          圖片中兩件事情值得注意:

          • 我直接在函數(shù)名之后(在 \< 和 > 之間)指定了類型。在本例中,由于函數(shù)簽名足夠簡單,我們其實可以省略這部分來調(diào)用函數(shù)而編譯器將會從所傳參數(shù)推斷出類型。然而,如果你把單詞?number?改為?string?則整個例子將不再工作。
          • 現(xiàn)在無法打印出?length?屬性了,因為數(shù)字沒有這個屬性。

          這正是你期待一個強(qiáng)類型語言該做的事情,并且這也是當(dāng)定義 通用的 行為時為何你要使用泛型的原因。

          我還能用泛型做些什么?

          前面的例子常被稱為泛型的 “Hello World”, 你能在任何一篇文章中找到它,但它是解釋泛型潛能的一個絕佳途徑。但還有些其他你能做到的有趣之事,當(dāng)然了總是在類型安全領(lǐng)域的,別忘了,你要構(gòu)建能在多種環(huán)境下復(fù)用的東西,同時還要努力保持住我們非常關(guān)心的類型信息。

          自動結(jié)構(gòu)檢查

          泛型中的這一點無疑是我最喜歡的了。考慮如下場景:你有一個固定的結(jié)構(gòu)(即一個對象)并且你在試圖動態(tài)地訪問其中一個屬性。我們之前已經(jīng)像這樣完成了這個功能:

          function?get(obj,?prop)?{
          ??if(!obj[prop])?return?null;
          ??return?obj[prop]
          }

          我并沒有用到?hasOwnProperty?或其他類似的技術(shù),但你能明白要點就好,你需要執(zhí)行一個基礎(chǔ)的結(jié)構(gòu)檢查以確保能控制所訪問的屬性不屬于對象的情況。現(xiàn)在,讓我們將其轉(zhuǎn)換為類型安全的 TypeScript 并看看泛型能如何幫助我們:

          type?Person?=?{
          ????name:?string,
          ????age:?number,
          ????city:?string
          }

          function?getPersonProp<K?extends?keyof?Person>(p:Person,?key:?K):?any?{
          ????return?p[key]
          }

          現(xiàn)在,請注意我是如何使用泛型符號的:我不是僅聲明了一個泛型 K,同時還說明了它 繼承自 Person 中的鍵類型。這太棒了!你可以聲明式的界定你傳入的值會匹配字符串?nameage?或?city。本質(zhì)上你聲明了一個枚舉值,而當(dāng)你這么想的時候,就沒之前那么興奮了吧。但你也不用止步于此,可以通過像這樣重新定義該函數(shù)來重燃激情:

          function?get<T,?K?extends?keyof?T>(p:?T,?key:?K):?any?{
          ????return?p[key]
          }

          這就對了,我們現(xiàn)在有了兩個泛型,后一個被聲明為繼承自前一個中的鍵,但本質(zhì)上的好處是你現(xiàn)在不再受限于某一種具體類型(即?Person?類型的對象) 了,該函數(shù)可被你放心大膽地用于任何類型或結(jié)構(gòu)了。

          下面是當(dāng)你用一個非法屬性名使用它時將會發(fā)生的:

          泛型類(Generic classes)

          泛型不僅應(yīng)用于函數(shù)簽名,亦可用來定義你自己的泛型類。這提供了將通用邏輯封裝進(jìn)可復(fù)用構(gòu)造中的能力,讓一些有意思的行為變得可能。

          下面是一個例子:

          abstract?class?Animal?{
          ????handle()?{?throw?new?Error("Not?implemented")?}
          }

          class?Horse?extends?Animal{
          ????color:?string
          ????handle()?{
          ????????console.log("Riding?the?horse...")
          ????}
          }

          class?Dog?extends?Animal{
          ????name:?string?
          ????handle()?{
          ????????console.log("Feeding?the?dog...")
          ????}
          }

          class?Handlerextends?Animal>?{
          ????animal:?T

          ????constructor(animal:?T)?{
          ????????this.animal?=?animal
          ????}

          ????handle()?{
          ????????this.animal.handle()
          ????}
          }

          class?DogHandler?extends?Handler?{}
          class?HorseHandler?extends?Handler?{}

          在本例中,我們定義了一個可以處理任意動物類型的處理類,雖說不用泛型也能做到,但使用泛型的益處在最后兩行顯而易見。這是因為借助泛型,處理類邏輯完全被封裝進(jìn)了一個泛型類中,從而我們可以約束類型并創(chuàng)建指定類型的類,這樣的類只對動物類型生效。你也可以在此添加額外的行為,而類型信息也得以保留。

          來自這個例子的另一個收獲是,泛型可被約束為僅繼承自指定的一組類型。正如你所見,T?只能是?Dog?或?Horse?而非其他。

          可變參數(shù)元組(Variadic Tuples)

          實際上這是 TypeScript 4.0 中的新特性。并且盡管我 [已經(jīng)在這篇文章中介紹了它][Link 2],此處仍會快速回顧一下。

          概況來說,可變參數(shù)元組帶來的,是用泛型定義某元組中一個可變的部分,默認(rèn)情況下這部分什么都沒有。

          一個普通的元組定義將產(chǎn)生一個固定尺寸的數(shù)組,其所有元素都是預(yù)定義好的類型:

          type?MyTuple?=?[string,?string,?number]

          let?myList:MyTuple?=?["Fernando",?"Doglio",?37]

          現(xiàn)在,歸功于泛型和可變參數(shù)元組,你可以這樣做:

          type?MyTupleextends?unknown[]>?=?[string,?string,?...T,?number]

          let?myList:MyTuple<[boolean,?number]>?=?["Fernando",?"Doglio",?true,?3,?37]
          let?myList:MyTuple<[number,?number,?number]>?=?["Fernando",?"Doglio",?1,2,3,4]

          如果你注意看,我們使用了一個泛型?T(繼承自一個?unknown?數(shù)組)用以將一個可變部分置于元組中。因為?T?是?unknown?類型的一個列表,你可以在里面裝任何東西。比分說,你可以將其定義為單一類型的一個列表,就像這樣:

          type?anotherTuple?=?[boolean,?...T,?boolean];

          let?oneNumber:?anotherTuple<[number]>?=?[true,?1,?true];
          let?twoNumbers:?anotherTuple<[number,?number]>?=?[true,?1,?2,?true]
          let?manyNumbers:?anotherTuple<[number,?number,?number,?number]>?=?[true,?1,?2,?3,?4,?true]

          天高任鳥飛,本質(zhì)上你可以定義出一種模板元組的形式,以供稍后隨意(當(dāng)然要按照你設(shè)置的模板)使用。

          總結(jié)

          泛型是一種非常強(qiáng)大的工具,雖然有時閱讀由其編寫的代碼宛如天書,但熟能生巧。慢慢品味,用心閱讀,你將看到其內(nèi)在的潛能。

          ??愛心三連擊

          1.看到這里了就點個在看支持下吧,你的點贊在看是我創(chuàng)作的動力。

          2.關(guān)注公眾號程序員成長指北,回復(fù)「1」加入高級前端交流群!「在這里有好多 前端?開發(fā)者,會討論?前端 Node 知識,互相學(xué)習(xí)」!

          3.也可添加微信【ikoala520】,一起成長。

          “在看轉(zhuǎn)發(fā)”是最大的支持


          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天干在线观看视频 | 麻豆福利视频 | 国产第99页 | 99热在线观看8 | 欧美精品久久久久久久久大尺度 |