<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      TypeScript(十四)變體(協(xié)變與逆變)

      目錄

      前言

      "鴨子類(lèi)型"

      子類(lèi)型化

      定義

      特點(diǎn)

      賦值兼容性

      反身性

      傳遞性

      協(xié)變

      逆變

      雙變

      不變

      思考

      看個(gè)例子

      原因是什么?

      返回值

      參數(shù)

      總結(jié)

      相關(guān)文章


      前言

      本文收錄于TypeScript知識(shí)總結(jié)系列文章,歡迎指正! 

      第一次接觸到變體這個(gè)概念是在深入理解TypeScript中,類(lèi)型之間的轉(zhuǎn)換稱(chēng)為變體或者變型,在TS中,類(lèi)型之間能否互相賦值,會(huì)不會(huì)報(bào)錯(cuò),安不安全這些都與變體有關(guān)。本文將帶大家了解ts中的變體

      在Java中,每一個(gè)類(lèi)都是一個(gè)個(gè)體,比如,我們定義了一個(gè)Dog和Cat兩個(gè)類(lèi),這二者的結(jié)構(gòu)相同。

      // Dog.java
      public class Dog {
          String color;
          int age;
      }
      
      // Cat.java
      public class Cat {
          String color;
          int age;
      }
      // Main.java
      public class Main {
          Dog myCat = new Cat();// cannot convert from Cat to Dog
          Cat myDog = new Dog();// cannot convert from Dog to Cat
          Cat myCat2 = new Cat();// 允許
          Dog myDog2 = new Dog();// 允許
      }

      此時(shí)使用控制變量對(duì)其二者進(jìn)行實(shí)例化,可以看到,雖然Dog和Cat的屬性相同,都有color以及age屬性,但是不能互相聲明類(lèi)型及賦

      "鴨子類(lèi)型"

      鴨子類(lèi)型(duck typing)是一種動(dòng)態(tài)類(lèi)型機(jī)制的編程概念,它指的是一個(gè)對(duì)象的類(lèi)型由它能干什么決定,而不是它屬于什么類(lèi)決定,它在運(yùn)行時(shí)就會(huì)對(duì)對(duì)象的類(lèi)型進(jìn)行判斷。鴨子類(lèi)型的思想是:如果它看起來(lái)像鴨子,游起來(lái)像鴨子,叫起來(lái)像鴨子,那么它就是鴨子。

      TS中的類(lèi)型檢查就是鴨子類(lèi)型系統(tǒng),如果兩個(gè)對(duì)象類(lèi)型擁有相同的屬性或方法,那么它們就被認(rèn)定為是相同類(lèi)型

      我們?cè)赥S中實(shí)現(xiàn)上面的效果

      class Dog {
          color: string
          age: number
      }
      class Cat {
          color: string
          age: number
      }
      
      const dog: Cat = new Dog()// 允許
      const cat: Dog = new Cat()// 允許

      可以看到,TypeScript和JAVA的模式不同,只要結(jié)構(gòu)相同就可以重復(fù)使用類(lèi)型,這與其他語(yǔ)言不太一樣。

      子類(lèi)型化

      在學(xué)習(xí)數(shù)學(xué)的集合時(shí),一個(gè)集合A是另一個(gè)集合B的子集,那么A可以被視為B的一個(gè)子類(lèi)型;正方形可以視作矩形的一種子類(lèi),因?yàn)樗^承了矩形的性質(zhì),同時(shí)又在矩形的基礎(chǔ)上增加了一些附加的約束條件

      定義

      在計(jì)算機(jī)科學(xué)中,我們常說(shuō)的類(lèi)的繼承是對(duì)父類(lèi)的拓展,而子類(lèi)型化(Subtyping)是父類(lèi)型的拓展,它是指一種類(lèi)型(子類(lèi)型)是另一種類(lèi)型(超類(lèi)型)的一種特殊形式。與繼承相同,它可以添加或覆蓋超類(lèi)型的屬性和方法。它是較為抽象的拓展方式,沒(méi)有拓展具體的值,而是拓展類(lèi)型。超類(lèi)型經(jīng)過(guò)子類(lèi)型化操作后的類(lèi)型產(chǎn)物稱(chēng)為子類(lèi)型。

      特點(diǎn)

      賦值兼容性

      如果IDog是IAnimal的子類(lèi)型,那么IDog類(lèi)型的變量可以賦值給IAnimal類(lèi)型的變量,舉個(gè)例子說(shuō)說(shuō)

      interface IAnimal {
          name: string
      }
      interface IDog extends IAnimal {
          color: string
      }
      const animal: IAnimal = {
          name: "阿黃"
      }
      const dog: IDog = Object.assign(animal, {
          color: "black"
      })
      const _animal: IAnimal = dog // 可以執(zhí)行

      其中IDog繼承自IAnimal,如果使用變量將這兩種類(lèi)型實(shí)現(xiàn),則可以將子類(lèi)型的變量賦予給父類(lèi)型的變量。

      反身性

      任何類(lèi)型都是它自己的子類(lèi)型。

      interface IAnimal {
          name: string
      }
      const animal: IAnimal = {
          name: "阿黃"
      }
      const _animal: IAnimal = animal

      傳遞性

      如果IDog是IAnimal的子類(lèi)型,IWhiteDog是IDog的子類(lèi)型,那么IWhiteDog也是IAnimal的子類(lèi)型

      interface IAnimal {
          name: string
      }
      interface IDog extends IAnimal {
          color: string
      }
      interface IWhiteDog extends IDog {
          isWhite: boolean
      }
      const animal: IAnimal = {
          name: "阿黃"
      }
      const dog: IDog = Object.assign(animal, {
          color: "black"
      })
      const whiteDog: IWhiteDog = Object.assign(dog, {
          isWhite: true
      })
      const _animal: IAnimal = whiteDog // 可以執(zhí)行

      除了上述幾點(diǎn)外,子類(lèi)型還有變量協(xié)變性及函數(shù)參數(shù)逆變性的特點(diǎn),我們?cè)谙挛闹姓归_(kāi)講講

      協(xié)變

      如果理解了上面的鴨子類(lèi)型和子類(lèi)型化的概念,協(xié)變(Covariance)就不難理解。它是一種類(lèi)型的轉(zhuǎn)換,就像子類(lèi)型化中的例子,如果類(lèi)型IDog是類(lèi)型IAnimal的子類(lèi)型,那么dog可以賦值給animal,這個(gè)過(guò)程就是協(xié)變,即協(xié)變多變少(子類(lèi)型賦值給父類(lèi)型)

      為了更好理解上面的例子以及后續(xù)的案例,我們寫(xiě)一個(gè)工具類(lèi)型IsExtends,用于判斷兩個(gè)類(lèi)型之間是否是類(lèi)型的繼承關(guān)系

      type IsExtends<Son, Parent> = Son extends Parent ? true : false;
      type animalExtendsDog = IsExtends<IAnimal, IDog>// false
      type dogExtendsAnimal = IsExtends<IDog, IAnimal>// true

      逆變

      逆變(Contravariance)與協(xié)變相反,如果將父類(lèi)賦值給子類(lèi)成立,則稱(chēng)為逆變。我們將上述的例子修改一下變成以下代碼,就是逆變的過(guò)程,即逆變少變多(父類(lèi)型賦值給子類(lèi)型)

      const _dog: IDog = animal // 無(wú)法執(zhí)行,類(lèi)型 "IAnimal" 中缺少屬性 "color",但類(lèi)型 "IDog" 中需要該屬性。

      然而,一般情況下上面這段代碼是會(huì)拋錯(cuò)的,提示缺少屬性,此時(shí)我們可以借助類(lèi)型斷言進(jìn)行轉(zhuǎn)換

      const _dog: IDog = animal as IDog // 可以執(zhí)行

      但是這么寫(xiě)不太安全,如果animal中缺少I(mǎi)Dog的屬性可能會(huì)拋錯(cuò)

      除此之外,函數(shù)的參數(shù)具有逆變性(不進(jìn)行類(lèi)型檢查就具有雙變特征),我們可以借助TS的函數(shù)進(jìn)行轉(zhuǎn)變,首先我們寫(xiě)個(gè)工具類(lèi)型ToFun,將類(lèi)型作為參數(shù)傳入函數(shù)中

      type ToFun<P> = (params: P) => void

      然后我們將之前兩個(gè)類(lèi)型IAnimal和IDog傳入函數(shù)中就會(huì)發(fā)現(xiàn)結(jié)果與前文截然相反

      type IsExtends<Son, Parent> = Son extends Parent ? true : false;
      type ToFun<P> = (params: P) => void
      
      type animalExtendsDogFn = IsExtends<ToFun<IAnimal>, ToFun<IDog>>// true
      type dogExtendsAnimalFn = IsExtends<ToFun<IDog>, ToFun<IAnimal>>// false

      tips:如果兩個(gè)都是true,可以將tsconfig中的strictFunctionTypes或strict打開(kāi),此時(shí)會(huì)檢測(cè)函數(shù)參數(shù)的變體

      此時(shí),我們可以將變量轉(zhuǎn)換為函數(shù)的形式,達(dá)到逆變的效果

      const animalFn: (animal: IAnimal) => void = (animal) => { }
      const _dog: ToFun<IDog> = animalFn // 可以執(zhí)行

      雙變

      雙變(Bivariance)在許多地方被稱(chēng)為是雙向協(xié)變,個(gè)人認(rèn)為不太準(zhǔn)確,雙變是指類(lèi)型同時(shí)具有協(xié)變和逆變的性質(zhì),稱(chēng)為協(xié)變與逆變可能比較合適。我們將tsconfig中的strictFunctionTypes與strict關(guān)閉,上面的例子就會(huì)顯示兩個(gè)true

      type IsExtends<Son, Parent> = Son extends Parent ? true : false;
      type ToFun<P> = (params: P) => void
      type animalExtendsDogFn = IsExtends<ToFun<IAnimal>, ToFun<IDog>>// true
      type dogExtendsAnimalFn = IsExtends<ToFun<IDog>, ToFun<IAnimal>>// true

      我們使用變量試試

      const animalFn: (animal: IAnimal) => void = (animal) => { }
      const dogFn: (dog: IDog) => void = (dog) => { }
      const _dog: ToFun<IDog> = animalFn // 可以執(zhí)行
      const _animal: ToFun<IAnimal> = dogFn // 可以執(zhí)行

      不變

      不變(Invariance)的概念就比較簡(jiǎn)單了,兩種類(lèi)型既不會(huì)發(fā)生協(xié)變,也不會(huì)逆變,完全沒(méi)有關(guān)系的兩種類(lèi)型

      interface IAnimal {
          name: string
      }
      interface IDog {
          color: string
      }
      
      let animal: IAnimal = {
          name: "阿黃"
      }
      let dog: IDog = {
          color: "black"
      }
      
      dog = animal// 不能執(zhí)行,缺少對(duì)應(yīng)屬性
      animal = dog// 不能執(zhí)行,缺少對(duì)應(yīng)屬性

      思考

      看個(gè)例子

      思考這個(gè)的例子,如果使用上面講到的協(xié)變與逆變的概念應(yīng)該不難理解。變量(返回值)協(xié)變,參數(shù)逆變

      我們把原文的例子使用TS實(shí)現(xiàn)一下

      首先新建三個(gè)類(lèi)型,分別是IAnimal,IDog,IWhiteDog,從動(dòng)物到白狗層層繼承

      interface IAnimal {
          name: string
      }
      interface IDog extends IAnimal {
          type: string
      }
      interface IWhiteDog extends IDog {
          isWhite: boolean
      }

      接著我們使用工具類(lèi)型實(shí)現(xiàn)一個(gè)函數(shù)創(chuàng)建,以及之前判斷繼承的工具

      type Fun<P, R> = (params: P) => R// 創(chuàng)建以P為參數(shù),R為返回值的函數(shù)
      type IsExtends<Son, Parent> = Son extends Parent ? true : false;// 是否是繼承關(guān)系

      最后使用上述兩種工具對(duì)類(lèi)型進(jìn)行檢測(cè):除了自身外,當(dāng)函數(shù)的參數(shù)是IAnimal,返回值是IWhiteDog類(lèi)型時(shí),此函數(shù)是IDogFn的子類(lèi)型

      type IDogFn = Fun<IDog, IDog> // IDog, IDog
      
      type IAnimalWhiteDogFn = Fun<IAnimal, IWhiteDog>// IAnimal, IWhiteDog
      type IAnimalFn = Fun<IAnimal, IAnimal>// IAnimal, IAnimal
      type IWhiteDogFn = Fun<IWhiteDog, IWhiteDog>// IWhiteDog, IWhiteDog
      type IWhiteDogAnimalFn = Fun<IWhiteDog, IAnimal>// IWhiteDog, IAnimal
      
      type IsExtendsAnimalWhiteDog = IsExtends<IAnimalWhiteDogFn, IDogFn>// true
      type IsExtendsAnimal = IsExtends<IAnimalFn, IDogFn>// false
      type IsExtendsWhiteDog = IsExtends<IWhiteDogFn, IDogFn>// false
      type IsExtendsWhiteDogAnimal = IsExtends<IWhiteDogAnimalFn, IDogFn>// false

      函數(shù)返回值和變量一樣是協(xié)變的,所以子類(lèi)遵循常規(guī)的繼承取IWhiteDog;參數(shù)是逆變的,因此與正常的繼承行為相反,取IAnimal

      原因是什么?

      返回值

      返回值是協(xié)變的這點(diǎn)不難理解,函數(shù)執(zhí)行時(shí)結(jié)果是RHS(Right Hand Side)右操作數(shù),即函數(shù)返回值同樣是賦值給變量的,我們還是使用上文賦值兼容性的例子做一點(diǎn)修改,在外層加個(gè)函數(shù)

      interface IAnimal {
          name: string
      }
      interface IDog extends IAnimal {
          color: string
      }
      const animal = () => ({
          name: "阿黃"
      })
      const dog = () => Object.assign(animal, {
          color: "black"
      })
      const _animal: typeof animal = dog // 可以執(zhí)行

      參數(shù)

      傳入的參數(shù)訪(fǎng)問(wèn)子類(lèi)型中的屬性是不安全的。我們針對(duì)參數(shù)的子類(lèi)型舉個(gè)例子,首先我們?cè)黾右粋€(gè)IBlackDog類(lèi)型,并將IDogFn以及其他的變量實(shí)現(xiàn)

      interface IAnimal {
          name: string
      }
      interface IDog extends IAnimal {
          type: string
      }
      interface IWhiteDog extends IDog {
          isWhite: boolean
      }
      interface IBlackDog extends IDog {
          isBlack: boolean
      }
      
      type Fun<P, R> = (params: P) => R// 函數(shù)
      type IDogFn = Fun<IDog, IDog> // dog函數(shù)
      const animal: IAnimal = {
          name: "阿黃"
      }
      const dog: IDog = Object.assign(animal, {
          type: "dog"
      })
      const whiteDog: IWhiteDog = Object.assign(dog, {
          isWhite: true
      })
      const blackDog: IBlackDog = Object.assign(dog, {
          isBlack: true
      })

      接著創(chuàng)建一個(gè)函數(shù)參數(shù)接收一個(gè)函數(shù),這個(gè)函數(shù)結(jié)構(gòu)是Fun<IDog, IDog>。此時(shí)由下面的代碼可以推導(dǎo)出,為何協(xié)變的參數(shù)是不安全的,可能有點(diǎn)繞

      const example = (_fn: IDogFn): void => {
          _fn(blackDog)// _fn參數(shù)限制了IDog類(lèi)型,所以實(shí)參可以傳遞blackDog,whiteDog,dog。這里我們傳入blackDog
      }
      example(whiteDogFn)// 拋錯(cuò),參數(shù)“_whiteDog”和“params” 的類(lèi)型不兼容。
      
      const whiteDogFn = (_whiteDog: IWhiteDog) => {
          _whiteDog.isWhite = false// 形參取IWhiteDog,但是實(shí)參傳了blackDog,此時(shí)就會(huì)拋錯(cuò),找不到isWhite,因?yàn)閎lackDog只有isBlack。所以使用形參使用IWhiteDog是不安全的,必須傳遞IDog類(lèi)型,或者IAnimal,因?yàn)镮Animal有的屬性,IDog都有
          return dog
      }
      const animalFn = (_animal: IAnimal) => {
          return dog
      }
      example(animalFn)// 允許執(zhí)行

      針對(duì)上面的代碼做個(gè)解釋?zhuān)?/strong>

      我們定義了一個(gè)函數(shù)whiteDogFn,接收一個(gè)參數(shù)IWhiteDog,此時(shí)我們可以直接調(diào)用IWhiteDog中的屬性isWhite,到這里都還算正常。但是接下來(lái)我們將這個(gè)函數(shù)代入到example函數(shù)中,為什么會(huì)拋錯(cuò)?因?yàn)镮DogFn類(lèi)型限制我們傳入blackDog,whiteDog,dog這三種類(lèi)型,如果此時(shí)我們傳入blackDog則會(huì)有問(wèn)題,因?yàn)閎lackDog沒(méi)有isWhite這個(gè)屬性。怎么做才能解決這個(gè)問(wèn)題呢?從源頭上控制函數(shù)的參數(shù)類(lèi)型,即使用逆變的方式限制whiteDogFn函數(shù)的參數(shù),限制為IAnimal,此時(shí)IAnimal只提供name這個(gè)屬性,我們只能調(diào)用這個(gè)屬性,并且這個(gè)屬性是IAnimal,IDog,IWhiteDog,IBlackDog這四個(gè)類(lèi)型都有的,此時(shí)使用該函數(shù)程序就是安全的

      總結(jié)

      以上就是文章全部?jī)?nèi)容了,本文詳細(xì)講述了TS中的變體概念,深入講解了子類(lèi)型化操作,協(xié)變,逆變,雙變,不變的概念,其中協(xié)變特點(diǎn)是多變少,逆變則是少變多,雙變即集成協(xié)變與逆變,不變則雙向都無(wú)法賦值,最后介紹了一下關(guān)于函數(shù)參數(shù)和返回值的變體規(guī)則,說(shuō)明了參數(shù)逆變與返回值協(xié)變的原因。

      感謝你的閱讀,如果覺(jué)得文章不錯(cuò)的話(huà),還希望支持一下博主!

      相關(guān)文章

      協(xié)變與逆變 | 深入理解 TypeScript

      javascript - 如何理解ts的函數(shù)參數(shù)雙向協(xié)變? - SegmentFault 思否

      聊聊TypeScript類(lèi)型兼容,協(xié)變、逆變、雙向協(xié)變以及不變性 - 掘金

      Covariance and Contravariance in TypeScript

      posted @ 2023-05-15 18:08  阿宇的編程之旅  閱讀(211)  評(píng)論(0)    收藏  舉報(bào)  來(lái)源
      主站蜘蛛池模板: 国产成人精品无缓存在线播放| 欧美和黑人xxxx猛交视频| 狠狠v日韩v欧美v| 国产极品美女高潮无套| 91色老久久精品偷偷蜜臀| 亚洲国产综合自在线另类| 国产精品国产三级国产试看| 九九热在线免费视频播放| 亚洲欭美日韩颜射在线二| 色妞www精品免费视频| 黑人巨大精品欧美| 中文字幕有码无码人妻在线 | 国产a在视频线精品视频下载| 色窝窝免费一区二区三区| 欧美人与动牲交A免费观看| 欧美人与性动交α欧美精品| 无码一区二区三区久久精品| 中文字幕国产精品专区| 亚洲色大成网站www久久九| 国产免费高清69式视频在线观看| 国产一区二区三区18禁| 成人片黄网站色大片免费| 亚洲精品视频一二三四区| 不卡高清AV手机在线观看| 广宗县| 内射一区二区三区四区| 国内揄拍国内精品对久久| 最好看的中文字幕国语| 亚洲国产精品综合久久20| 久久久无码精品亚洲日韩蜜臀浪潮| 亚洲VA久久久噜噜噜久久无码| 亚洲精品成人老司机影视| 成在线人永久免费视频播放| 久久亚洲色www成人| 海淀区| 久热久精久品这里在线观看 | 亚洲 中文 欧美 日韩 在线| 日本精品网| 亚洲精品国产字幕久久麻豆| 独山县| 丁香五月婷激情综合第九色|