請停止使用 TypeScript 接口
為什么應(yīng)該使用類型而不是接口
這張圖片是由人工智能生成的。
類型和接口 是每個 TypeScript 程序中使用的重要特性。
然而,由于類型和接口在功能上非常相似,這就引出了一個問題:哪個更好?
今天,我們將評估類型和接口,然后得出結(jié)論,說明為什么你在大多數(shù)情況下應(yīng)該使用類型而不是接口。
所以,不多說了……讓我們馬上開始吧。
那么它們有什么區(qū)別呢?
讓我們分析一下這個 Person 類型 和 接口 的定義:
type Person = {
name: string
age: number
}
interface Person {
name: string
age: number
}
很明顯,類型和接口有相似的語法,關(guān)鍵區(qū)別在于類型使用 = 來定義對象的形狀,而不是接口。
然而,事情遠(yuǎn)不止于此。
讓我們深入挖掘一下,一起探索和評估類型和接口。
可擴(kuò)展性
就可擴(kuò)展性而言,許多人認(rèn)為接口是顯而易見的贏家,因為接口可以使用 extends 擴(kuò)展其他接口。
// 可擴(kuò)展性示例
interface Job {
job: string
}
interface Person extends Job {
name: string
age: number
}
// 使用了 Person 和 Job 的屬性。
const person: Person = {
name: "John",
age: 25,
job: "全棧 Web 開發(fā)者",
}
在這里,Person 接口擴(kuò)展了 Job,因此 Job 接口的屬性合并到了 Person 中。
另一方面,類型也通過利用 聯(lián)合 | 或 交集 & 運算符來合并現(xiàn)有類型,提供了可擴(kuò)展性。
接口無法直接表達(dá)這種行為。
// ? 正常工作
type Person = {
name: string
age: number
} & { job: string }
// ? 不工作
interface Person {
name: string
age: number
} & { job: string }
實現(xiàn)
在 TypeScript 中,接口與面向?qū)ο缶幊蹋∣OP)兼容,就像其他語言(例如 Java 或 C#)一樣。
這意味著接口可以在類中使用 implements 實現(xiàn)。
現(xiàn)在讓我們將 Person 定義為一個類,并實現(xiàn)一個名為 Work 的新接口,滿足它們之間的約定。
// 實現(xiàn)示例
interface Work {
doWork: () => void
}
class Person implements Work {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// 實現(xiàn) doWork 方法以滿足 `Work` 接口。
doWork() {
console.log("工作中...")
}
}
const person = new Person("John", 25)
person.doWork()
因此,如果你經(jīng)常使用 OOP,接口將比類型更適用,因為類型不能直接由類實現(xiàn)。
性能
當(dāng)談?wù)撔阅軙r,我們指的是 TypeScript 編譯器執(zhí)行的 “類型檢查” 性能,隨著代碼庫規(guī)模的增加,其性能會呈指數(shù)級下降。
這就是為什么我們要對類型和接口在類型檢查性能方面進(jìn)行基準(zhǔn)測試,看看哪個更優(yōu)。
這是一個視頻,由 Matt Pocock 解釋了類型和接口之間的區(qū)別,以及在類型檢查性能方面實際上 沒有 類型和接口之間的任何差異。
接口為什么會有害
在 TypeScript 中,接口具有一個稱為 聲明合并 的獨特特性。
聲明合并是指 TypeScript 編譯器將 兩個或多個 具有相同名稱的接口合并為 一個。
// 初始的 Person 接口
interface Person {
name: string
age: number
}
// 使用 "聲明合并" 來完善 Person 接口
interface Person {
gender: string
}
// 使用 "合并" 接口來定義一個新的 "person"
const person: Person = { name: "John", age: 25, gender: "男性" }
一方面,這個特性允許方便地對現(xiàn)有接口進(jìn)行細(xì)化和擴(kuò)展,而不會影響其他依賴項。
另一方面,聲明合并可能會對你的代碼庫產(chǎn)生有害且令人驚訝的影響,主要有以下 兩個 原因:
- 優(yōu)先級順序:后面的聲明總是優(yōu)先于前面的聲明。如果不小心,當(dāng)在程序的許多部分進(jìn)行聲明合并時,這可能導(dǎo)致意外問題。
- 與類的不安全合并:由于 TypeScript 編譯器不檢查屬性的初始化,這可能導(dǎo)致意外的運行時錯誤。
類型沒有這個問題,因此更直接且安全。
結(jié)論
除非需要特定的接口行為,例如可擴(kuò)展的細(xì)化或使用 OOP 進(jìn)行實現(xiàn),否則最好使用類型。
類型靈活、直接,并且避免與聲明合并相關(guān)的問題。
與接口相比,類型在性能上也完全相同,為你提供了另一個選擇類型而不是接口的理由。