1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        TypeScript Interface vs Type知多少

        共 7694字,需瀏覽 16分鐘

         ·

        2021-05-21 02:57

        點(diǎn)擊上方 全棧前端精選,關(guān)注公眾號

        回復(fù)1,加入前端進(jìn)階交流群

        來源:陽呀呀

        https://segmentfault.com/a/1190000039834284

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

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

        1. 都可以用來描述對象或函數(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ò)展就是繼承,通過 extends 來實(shí)現(xiàn)。類型別名的擴(kuò)展就是交叉類型,通過 & 來實(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. 類型別名更通用(接口只能聲明對象,不能重命名基本類型)

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

        type A = number
        type B = A | string

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

        擴(kuò)展接口時,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'.
        }

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

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

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

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

        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)該用哪個

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

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

        以下是Preferring Interfaces Over Intersections的譯文:

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

        interface Foo { prop: string }

        type Bar = { prop: string };

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

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

        上述的幾個區(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

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

        再來看下面例子:

        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)錯中可以看出,當(dāng)使用接口時,報(bào)錯會準(zhǔn)確定位到Point。但是使用交叉類型時,雖然我們的 Point 交叉類型是 PointXY & PointZ, 但是在報(bào)錯的時候定位并不在 Point 中,而是在 Point3 中,即使我們的 Point 類型并沒有直接引用 Point3 類型。

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

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

        結(jié)論

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

        瀏覽 21
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产午夜精品一区二区三区免费 | 日本成人少妇 | 俺去也俺也去 | 性AV网 | 欧美日韩一区在线观看 | 超碰人人操91 | 成人色天堂| 加勒比综合网 | 欧美真人性爱精品 | www.豆花视频 |