<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 Interface vs Type知多少

          共 4607字,需瀏覽 10分鐘

           ·

          2021-04-19 15:46

          作者:陽(yáng)呀呀

          來(lái)源:SegmentFault




          接口和類型別名非常相似,在大多情況下二者可以互換。在寫TS的時(shí)候,想必大家都問(wèn)過(guò)自己這個(gè)問(wèn)題,我到底應(yīng)該用哪個(gè)呢?希望看完本文會(huì)給你一個(gè)答案。知道什么時(shí)候應(yīng)該用哪個(gè),首先應(yīng)該了解二者之間的相同點(diǎn)和不同點(diǎn),再做出選擇。


          接口 vs 類型別名 相同點(diǎn)


          1. 都可以用來(lái)描述對(duì)象或函數(shù)

          interface Point {
          x: number
          y: number
          }

          interface SetPoint {
          (x: number, y: number): void;
          }

          type Point = {
          x: number;
          y: number;
          };

          type SetPoint = (x: number, y: number) => void;

          2. 都可以擴(kuò)展

          兩者的擴(kuò)展方式不同,但并不互斥。接口可以擴(kuò)展類型別名,同理,類型別名也可以擴(kuò)展接口。

          接口的擴(kuò)展就是繼承,通過(guò) extends 來(lái)實(shí)現(xiàn)。類型別名的擴(kuò)展就是交叉類型,通過(guò) & 來(lái)實(shí)現(xiàn)。

          // 接口擴(kuò)展接口
          interface PointX {
          x: number
          }

          interface Point extends PointX {
          y: number
          }

          // 類型別名擴(kuò)展類型別名
          type PointX = {
          x: number
          }

          type Point = PointX & {
          y: number
          }

          // 接口擴(kuò)展類型別名
          type PointX = {
          x: number
          }
          interface Point extends PointX {
          y: number
          }

          // 類型別名擴(kuò)展接口
          interface PointX {
          x: number
          }
          type Point = PointX & {
          y: number
          }


          接口 vs 類型別名不同點(diǎn)


          1. 類型別名更通用(接口只能聲明對(duì)象,不能重命名基本類型)

          類型別名的右邊可以是任何類型,包括基本類型、元祖、類型表達(dá)式(&|等類型運(yùn)算符);而在接口聲明中,右邊必須為結(jié)構(gòu)。例如,下面的類型別名就不能轉(zhuǎn)換成接口:

          type A = number
          type B = A | string

          2. 擴(kuò)展時(shí)表現(xiàn)不同

          擴(kuò)展接口時(shí),TS將檢查擴(kuò)展的接口是否可以賦值給被擴(kuò)展的接口。舉例如下:

          interface A {
          good(x: number): string,
          bad(x: number): string
          }
          interface B extends A {
          good(x: string | number) : string,
          bad(x: number): number // Interface 'B' incorrectly extends interface 'A'.
          // Types of property 'bad' are incompatible.
          // Type '(x: number) => number' is not assignable to type '(x: number) => string'.
          // Type 'number' is not assignable to type 'string'.
          }

          但使用交集類型時(shí)則不會(huì)出現(xiàn)這種情況。我們將上述代碼中的接口改寫成類型別名,把 extends 換成交集運(yùn)算符 &,TS將盡其所能把擴(kuò)展和被擴(kuò)展的類型組合在一起,而不會(huì)拋出編譯時(shí)錯(cuò)誤。

          type A = {
          good(x: number): string,
          bad(x: number): string
          }
          type B = A & {
          good(x: string | number) : string,
          bad(x: number): number
          }

          3. 多次定義時(shí)表現(xiàn)不同

          接口可以定義多次,多次的聲明會(huì)合并。但是類型別名如果定義多次,會(huì)報(bào)錯(cuò)。

          interface Point {
          x: number
          }
          interface Point {
          y: number
          }
          const point: Point = {x:1} // Property 'y' is missing in type '{ x: number; }' but required in type 'Point'.

          const point: Point = {x:1, y:1} // 正確

          type Point = {
          x: number // Duplicate identifier 'A'.
          }

          type Point = {
          y: number // Duplicate identifier 'A'.
          }

          到底應(yīng)該用哪個(gè)

          如果接口和類型別名都能滿足的情況下,到底應(yīng)該用哪個(gè)是我們關(guān)心的問(wèn)題。感覺(jué)哪個(gè)都可以,但是強(qiáng)烈建議大家只要能用接口實(shí)現(xiàn)的就優(yōu)先使用接口,接口滿足不了的再用類型別名。

          為什么會(huì)這么建議呢?其實(shí)在TS的wiki中有說(shuō)明。具體的文章地址在這里

          以下是Preferring Interfaces Over Intersections的譯文:

          大多數(shù)時(shí)候,對(duì)于聲明一個(gè)對(duì)象,類型別名和接口表現(xiàn)的很相似。

          interface Foo { prop: string }

          type Bar = { prop: string };

          然而,當(dāng)你需要通過(guò)組合兩個(gè)或者兩個(gè)以上的類型實(shí)現(xiàn)其他類型時(shí),可以選擇使用接口來(lái)擴(kuò)展類型,也可以通過(guò)交叉類型(使用 & 創(chuàng)造出來(lái)的類型)來(lái)完成,這就是二者開(kāi)始有區(qū)別的時(shí)候了。

          • 接口會(huì)創(chuàng)建一個(gè)單一扁平對(duì)象類型來(lái)檢測(cè)屬性沖突,當(dāng)有屬性沖突時(shí)會(huì)提示,而交叉類型只是遞歸的進(jìn)行屬性合并,在某種情況下可能產(chǎn)生 never 類型
          • 接口通常表現(xiàn)的更好,而交叉類型做為其他交叉類型的一部分時(shí),直觀上表現(xiàn)不出來(lái),還是會(huì)認(rèn)為是不同基本類型的組合
          • 接口之間的繼承關(guān)系會(huì)緩存,而交叉類型會(huì)被看成組合起來(lái)的一個(gè)整體
          • 在檢查一個(gè)目標(biāo)交叉類型時(shí),在檢查到目標(biāo)類型之前會(huì)先檢查每一個(gè)組分

          上述的幾個(gè)區(qū)別從字面上理解還是有些繞,下面通過(guò)具體的列子來(lái)說(shuō)明。

          interface Point1 {
          x: number
          }

          interface Point extends Point1 {
          x: string // Interface 'Point' incorrectly extends interface 'Point1'.
          // Types of property 'x' are incompatible.
          // Type 'string' is not assignable to type 'number'.
          }

          type Point1 = {
          x: number
          }

          type Point2 = {
          x: string
          }

          type Point = Point1 & Point2 // 這時(shí)的Point是一個(gè)'number & string'類型,也就是never

          從上述代碼可以看出,接口繼承同名屬性不滿足定義會(huì)報(bào)錯(cuò),而相交類型就是簡(jiǎn)單的合并,最后產(chǎn)生了 number & string 類型,可以解釋譯文中的第一點(diǎn)不同,其實(shí)也就是我們?cè)诓煌c(diǎn)模塊中介紹的擴(kuò)展時(shí)表現(xiàn)不同。

          再來(lái)看下面例子:

          interface PointX {
          x: number
          }

          interface PointY {
          y: number
          }

          interface PointZ {
          z: number
          }

          interface PointXY extends PointX, PointY {
          }

          interface Point extends PointXY, PointZ {

          }
          const point: Point = {x: 1, y: 1} // Property 'z' is missing in type '{ x: number; y: number; }' but required in type 'Point'

          type PointX = {
          x: number
          }

          type PointY = {
          y: number
          }

          type PointZ = {
          z: number
          }

          type PointXY = PointX & PointY

          type Point = PointXY & PointZ

          const point: Point = {x: 1, y: 1} // Type '{ x: number; y: number; }' is not assignable to type 'Point'.
          // Property 'z' is missing in type '{ x: number; y: number; }' but required in type 'Point3'.

          從報(bào)錯(cuò)中可以看出,當(dāng)使用接口時(shí),報(bào)錯(cuò)會(huì)準(zhǔn)確定位到Point。
          但是使用交叉類型時(shí),雖然我們的 
          Point 交叉類型是 PointXY & PointZ, 但是在報(bào)錯(cuò)的時(shí)候定位并不在 Point 中,而是在 Point3 中,即使我們的 Point 類型并沒(méi)有直接引用 Point3 類型。

          如果我們把鼠標(biāo)放在交叉類型 Point 類型上,提示的也是 type Point = PointX & PointY & PointZ,而不是 PointXY & PointZ

          這個(gè)例子可以同時(shí)解釋譯文中第二個(gè)和最后一個(gè)不同點(diǎn)。


          結(jié)論



          有的同學(xué)可能會(huì)問(wèn),如果我不需要組合只是單純的定義類型的時(shí)候,是不是就可以隨便用了。但是為了代碼的可擴(kuò)展性,建議還是優(yōu)先使用接口。現(xiàn)在不需要,誰(shuí)能知道后續(xù)需不需要呢?所以,讓我們大膽的使用接口吧~



          點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開(kāi)更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -


          瀏覽 39
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  中国操逼大片 | 亚洲免费色情 | 操女人视频网站 | 国产成人无码Av片小说在线观看 | 色一色综合网 |