TypeScript 中 Interface 與 Type 的區(qū)別?該用哪個比較好?
回復(fù)交流,加入前端編程面試算法每日一題群

接口 與 類型別名 的異同點
相同點
1. 都可以描述對象或函數(shù)
// 接口
interface Sister {
name: string;
age: number;
}
interface SetSister {
(name: string, age: number): void;
}
// 類型別名
type Sister = {
name: string;
age: number;
};
type SetSister = (name: string, age: number) => void;
2. 都可以擴展
interface 和 type 可以混合擴展,也就是說 interface 可以擴展 type,type 也可以擴展 interface。
但需要注意的是,接口的擴展是繼承( extends )。類型別名的擴展就是交叉類型(通過 & 實現(xiàn))
// 接口
interface SisterAn {
name: string;
}
// 類型別名
type SisterRan = {
age: number;
}
// 接口擴展接口
interface Sister extends SisterAn {
age: number;
}
// 類型別名擴展類型別名
type SisterPro = SisterRan & {
name: string;
}
// 接口擴展類型別名
interface Sister extends SisterRan {
name: string;
}
// 類型別名擴展接口
type SisterPro = SisterAn & {
age: number;
}
區(qū)別
官方 中這樣介紹兩者的區(qū)別:
Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an
interfaceare available intype, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.
意思就是說幾乎接口的所有特性都可以通過類型別名來實現(xiàn),主要區(qū)別在于:
1. 不同的聲明范圍
與接口不同,可以為任意的類型創(chuàng)建類型別名
類型別名的右邊可以是任何類型,包括基本類型、元祖、類型表達式( & 或 | 等);而在接口聲明中,右邊必須為變量結(jié)構(gòu)。例如,下面的類型別名就不能轉(zhuǎn)換成接口
type Name = string
type Text = string | { text: string };
type Coordinates = [number, number];
2. 不同的擴展形式
接口是通過繼承的方式來擴展,類型別名是通過 & 來擴展
// 接口擴展
interface SisterAn {
name: string;
}
interface Sister extends SisterAn {
age: number;
}
// 類型別名擴展
type SisterRan = {
age: number;
}
type SisterPro = SisterRan & {
name: string;
}
這里需要注意的是,接口擴展時,typescript 會檢查擴展的接口是否可以賦值給被擴展的接口
// 接口擴展
interface SisterAn {
name: string;
age: string
}
interface Sister extends SisterAn {
name: string;
age: number;
}
// 報錯:
// Interface 'Sister' incorrectly extends interface 'SisterAn'.
// Types of property 'age' are incompatible.
// Type 'number' is not assignable to type 'string'
但使用交集類型時就不會出現(xiàn)這種情況
// 類型別名擴展
type SisterRan = {
name: string;
age: string;
}
type SisterPro = SisterRan & {
name: string;
age: number;
}
類型別名擴展時,typescript 將盡其所能把擴展和被擴展的類型組合在一起,而不會拋出編譯時錯誤
3. 不同的重復(fù)定義表現(xiàn)形式
接口可以定義多次,多次的聲明會自動合并
interface Sister {
name: string;
}
interface Sister {
age: number;
}
const sisterAn: Sister = {
name: 'sisterAn'
}
// 報錯:Property 'age' is missing in type '{ name: string; }' but required in type 'Sister'
const sisterRan: Sister = {
name: 'sisterRan',
age: 12
}
// 正確
但是類型別名如果定義多次,會報錯
type Sister = { // Duplicate identifier 'Sister'
name: string;
}
type Sister = { // Duplicate identifier 'Sister'
age: number;
}
如何選擇 Interface 、 Type
雖然 官方 中說幾乎接口的所有特性都可以通過類型別名來實現(xiàn),但建議優(yōu)先選擇接口,接口滿足不了再使用類型別名,在 typescript 官網(wǎng) Preferring Interfaces Over Intersections 有說明,具體內(nèi)容如下:
大多數(shù)時候,對象類型的簡單類型別名的作用與接口非常相似
interface Foo { prop: string }
type Bar = { prop: string };但是,一旦你需要組合兩個或多個類型來實現(xiàn)其他類型時,你就可以選擇使用接口擴展這些類型,或者使用類型別名將它們交叉在一個中(交叉類型),這就是差異開始的時候。
接口創(chuàng)建一個單一的平面對象類型來檢測屬性沖突,這通常很重要! 而交叉類型只是遞歸的進行屬性合并,在某種情況下可能產(chǎn)生 never類型接口也始終顯示得更好,而交叉類型做為其他交叉類型的一部分時,直觀上表現(xiàn)不出來,還是會認為是不同基本類型的組合。 接口之間的類型關(guān)系會被緩存,而交叉類型會被看成組合起來的一個整體。 最后一個值得注意的區(qū)別是,在檢查到目標(biāo)類型之前會先檢查每一個組分。 出于這個原因,建議使用接口/擴展擴展類型而不是創(chuàng)建交叉類型。
- type Foo = Bar & Baz & {
- someProp: string;
- }
+ interface Foo extends Bar, Baz {
+ someProp: string;
+ }
簡單的說,接口更加符合 JavaScript 對象的工作方式,簡單的說明下,當(dāng)出現(xiàn)屬性沖突時:
// 接口擴展
interface Sister {
sex: number;
}
interface SisterAn extends Sister {
sex: string;
}
// index.ts(5,11): error TS2430: Interface 'SisterAn' incorrectly extends interface 'Sister'.
// Types of property 'sex' are incompatible.
// Type 'string' is not assignable to type 'number'.
// 交叉類型
type Sister1 = {
sex: number;
}
type Sister2 = {
sex: string;
}
type SisterAn = Sister1 & Sister2;
// 不報錯,此時的 SisterAn 是一個'number & string'類型,也就是 never
來源:https://github.com/Advanced-Frontend/Daily-Interview-Question
最后
號內(nèi)回復(fù):
120 套模版