TypeScript 中 Interface 與 Type 的區(qū)別?該用哪個比較好?
本文轉(zhuǎn)載自微信公眾號「三分鐘學前端」,作者sisterAn。轉(zhuǎn)載本文請聯(lián)系三分鐘學前端公眾號。
接口 與 類型別名 的異同點
相同點
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 interface are available in type, 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ū)別是,在檢查到目標類型之前會先檢查每一個組分。
出于這個原因,建議使用接口/擴展擴展類型而不是創(chuàng)建交叉類型。
- - type Foo = Bar & Baz & {
- - someProp: string;
- - }
- + interface Foo extends Bar, Baz {
- + someProp: string;
- + }
簡單的說,接口更加符合 JavaScript 對象的工作方式,簡單的說明下,當出現(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