<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知多少

          共 7781字,需瀏覽 16分鐘

           ·

          2021-05-15 18:05

          點擊上方 程序員成長指北,關注公眾號

          回復1,加入Node進階交流群

          來源:陽呀呀

          https://segmentfault.com/a/1190000039834284

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

          接口 vs 類型別名 相同點

          1. 都可以用來描述對象或函數

          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. 都可以擴展

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

          接口的擴展就是繼承,通過 extends 來實現。類型別名的擴展就是交叉類型,通過 & 來實現。

          // 接口擴展接口
          interface PointX {
              x: number
          }

          interface Point extends PointX {
              y: number
          }
          // 類型別名擴展類型別名
          type PointX = {
              x: number
          }

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

          接口 vs 類型別名不同點

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

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

          type A = number
          type B = A | string

          2. 擴展時表現不同

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

          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'.
          }

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

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

          3. 多次定義時表現不同

          接口可以定義多次,多次的聲明會合并。但是類型別名如果定義多次,會報錯。

          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ōu)先使用接口,接口滿足不了的再用類型別名。

          為什么會這么建議呢?其實在TS的wiki中有說明。具體的文章地址在這里。

          以下是Preferring Interfaces Over Intersections的譯文:

          大多數時候,對于聲明一個對象,類型別名和接口表現的很相似。

          interface Foo { prop: string }

          type Bar = { prop: string };

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

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

          上述的幾個區(qū)別從字面上理解還是有些繞,下面通過具體的列子來說明。

          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 // 這時的Point是一個'number & string'類型,也就是never

          從上述代碼可以看出,接口繼承同名屬性不滿足定義會報錯,而相交類型就是簡單的合并,最后產生了 number & string 類型,可以解釋譯文中的第一點不同,其實也就是我們在不同點模塊中介紹的擴展時表現不同。

          再來看下面例子:

          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'.

          從報錯中可以看出,當使用接口時,報錯會準確定位到Point。但是使用交叉類型時,雖然我們的 Point 交叉類型是 PointXY & PointZ, 但是在報錯的時候定位并不在 Point 中,而是在 Point3 中,即使我們的 Point 類型并沒有直接引用 Point3 類型。

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

          這個例子可以同時解釋譯文中第二個和最后一個不同點。

          結論

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

          如果覺得這篇文章還不錯
          點擊下面卡片關注我
          來個【分享、點贊、在看】三連支持一下吧

             “分享、點贊在看” 支持一波  

          瀏覽 43
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  韩国精品视频一区二区三区 | 草草影院国产第一页 | 国产毛片A级久久久久久 | 日本一级特黄A片 | 亚洲最大的黄色网址 |