TypeScript 中的類和接口


在面向對象(OOP)編程中,經常會使用到class(類)和interface(接口)。在TypeScript(以下簡稱TS)中也引入了類和接口的概念,使得TS強大的類型檢測機制更加完善。就像我們所知道的,一個類是一堆抽象概念的集合,我們可以從類的構造方法中創建出享有共同屬性和方法的對象。一個接口所描述的是一個對象相關的屬性和方法,但並不提供具體創建此對象實例的方法。

我們的前端項目使用Angular2.0+作為技術棧,Angular2.0+基於TS實現,我們在對代碼中某些部分添加類型注釋的時,經常出現會面對這樣的問題:

"我應該使用接口還是類來對當前數據進行類型注釋?

 

一個例子

在開發過程中經常會有這樣的操作

fetch('https://xxx.com/api/blabla')
    .then(data => {
       console.log(data);  // data: any
    });

  

以上代碼片段向后端API發起請求,得到返回的數據后我們會在前端UI中使用。

如果我們讓TS編譯目前這段代碼,then方法中的data參數將會默認被定義為any類型。因為在沒有類型定義的情況下,TS僅僅是通過分析代碼,判斷出類型應該是什么。

在這種情況下,我們為了提高程序的類型安全性,我們希望能夠主動地添加一個類型注釋`Response`,以便告知TS編譯器我們期望當前res參數的類型為什么。

// 需要在此處定義一個 `Response` 類型

fetch('https://xxx.com/api/blabla')
    .then((data: Response) => {
        console.log(data)  // data: Response
    })

  

由此,我們引出了本篇文章的核心問題,我們應該把`Response`類型定義為interface還是一個class呢?總感覺class和interface都可以。

 

TS中的interface

TS的核心原則職之一就是類型檢查,關注定義的值的數據結構

interface是僅存在於TS上下文中的一種虛擬結構,TS編譯器依賴接口用於類型檢查,最終編譯為JS后,接口將會被移除。

 

interface MyInterface {
  a: number;
  b: string;
}

  

以上MyInterface這個接口約定對象只能存在且必須有兩個屬性,這兩個屬性分別為數字 `a` 和 字符串 `b`,只要不遵守此約定,TS就會拋出錯誤。

 

TS中的class

與其他語言相比,JS並沒有直接對類的描述,基於原型的繼承方式也讓眾多的OOP世界的程序員充滿困惑,一直到了ES6,class關鍵字作為一種語法糖出現。

與interface不同,class作為TS的一種變量類型存在於上下文之中,class中可以提供,變量、方法等的具體實現方式等,它的作用不僅僅是約束數據結構。

class MyClass {
  a: number;
  b: string;
 
  constructor(options: MyInterface) {
    this.a = options.a;
    this.b = options.b;
  }
  
  foo(): void {
    console.log(this.a);
    console.log(this.b);
  }
}

  

以上MyClass類定義了兩個變量和一個foo方法,constructor方法會接受options參數初始化類中的屬性。

Class和Interface的比較

在TS中class和interface都可以用來約束數據的結構,但是頻繁使用class約束數據結構會使程序的性能受到影響,在 [typescript官網]() 的練習板塊中,我們在左邊書寫TS代碼,右邊會顯示所轉換成的JS代碼。

我們嘗試書寫class和interface看看兩者轉換后的代我們可以發現class編譯了大量代碼,但是interface並沒有轉換成任何JS,當我們定義大量的class,並且還有着復雜的繼承關系時,編譯過后的代碼體積將更加龐大。

最佳實踐

由於考慮到class和interface在TS中編譯結果的不同,我們面對不同的場景,使用正確的約束數據類型的方式,對我們代碼性能層面的提高就尤為重要。

什么時候使用class

當需要使用class時,我通常會考慮三個方面

  • 是否需要創建多個實例
  • 是否需要使用繼承
  • 是否需要特定的單例對象
什么時候使用interface

對於從服務器端獲取或者業務場景中模擬的數據,提倡使用interface去定義,這些數據通常是不會經常變化和調整的,這些數據可能僅僅只表示某些狀態,或者是UI上的文本。

interface配合class
class MyClass {
  a: number;
  b: string;
  origin: MyInterface;
 
  constructor(options: MyInterface) {
    this.a = options.a;
    this.b = options.b;
    this.origin = options;  // 保存原始的options數據
  }
  
  foo(): void {
    console.log(this.a);
    console.log(this.b);
  }
}
const a = new MyClass(options);
const b = new MyClass(a.origin);

  

在實際場景中,我們可以給class的參數指定好interface類型用來初始化class中的屬性,以上代碼在class的origin屬性中保存了options的數據,可以用來之后初始化全新的class實例,這解決了class實例在變化后很難clone出全新實例的問題,上面實例中 `b`變量 由 `a`變量 的orgin字段

初始化。

 

總之,當我們只是想要在TS中約束數據類型時,我們需要結合實際的場景去選擇使用class還是interface,如果只是一個簡單的后端請求,卻都使用class去約束,想象一下編譯出來一大堆無用的js代碼,簡直可怕ヽ(*。>Д<)o゜


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM