【TS】1322- type 和 interface 傻傻分不清楚?
阿寶哥精心準(zhǔn)備的《輕松學(xué) TypeScript》?視頻教程已經(jīng)更新到第十二期了,通過形象生動的動畫,讓你輕松搞懂 TypeScript 的難點(diǎn)和核心知識點(diǎn)!
如果你簡歷上的技能有寫 TypeScript,那么面試官可能會問你 type 和 interface 之間有什么區(qū)別?你知道怎么回答這個問題么?如果不知道的話,那看完本文也許你就懂了。
類型別名 type 可以用來給一個類型起個新名字,當(dāng)命名基本類型或聯(lián)合類型等非對象類型時(shí)非常有用:
type?MyNumber?=?number;
type?StringOrNumber?=?string?|?number;
type?Text?=?string?|?string[];
type?Point?=?[number,?number];
type?Callback?=?(data:?string)?=>?void;
在 TypeScript 1.6 版本,類型別名開始支持泛型。我們工作中常用的 Partial、Required、Pick、Record 和 Exclude 等工具類型都是以 type 方式來定義的。
//?lib.es5.d.ts
type?Partial?=?{
????[P?in?keyof?T]?:?T[P];
};
type?Required?=?{
????[P?in?keyof?T]-?:?T[P];
};
type?Pickextends?keyof?T>?=?{
????[P?in?K]:?T[P];
};
type?Recordextends?keyof?any,?T>?=?{
????[P?in?K]:?T;
};
type?Exclude?=?T?extends?U???never?:?T;
而接口 interface 只能用于定義對象類型,Vue 3 中的 App 對象就是使用 interface 來定義的:
//?packages/runtime-core/src/apiCreateApp.ts
export?interface?App?{
??version:?string
??config:?AppConfig
??use(plugin:?Plugin,?...options:?any[]):?this
??mixin(mixin:?ComponentOptions):?this
??component(name:?string):?Component?|?undefined?//?Getter
??component(name:?string,?component:?Component):?this?//?Setter
??directive(name:?string):?Directive?|?undefined
??directive(name:?string,?directive:?Directive):?this
}
由以上代碼可知,在定義接口時(shí),我們可以同時(shí)聲明對象類型上的屬性和方法。了解 type 和 interface 的作用之后,我們先來介紹一下它們的相似之處。
1、類型別名和接口都可以用來描述對象或函數(shù)
類型別名
type?Point?=?{
??x:?number;
??y:?number;
};
type?SetPoint?=?(x:?number,?y:?number)?=>?void;
在以上代碼中,我們通過 type 關(guān)鍵字為對象字面量類型和函數(shù)類型分別取了一個別名,從而方便在其他地方使用這些類型。
接口
interface?Point?{
??x:?number;
??y:?number;
}
interface?SetPoint?{
??(x:?number,?y:?number):?void;
}
2、類型別名和接口都支持?jǐn)U展
類型別名通過 &(交叉運(yùn)算符)來擴(kuò)展,而接口通過 extends 的方式來擴(kuò)展。
類型別名擴(kuò)展
type?Animal?=?{
??name:?string
}
type?Bear?=?Animal?&?{?
??honey:?boolean?
}
const?bear:?Bear?=?getBear()?
bear.name
bear.honey
接口擴(kuò)展
interface?Animal?{
??name:?string
}
interface?Bear?extends?Animal?{
??honey:?boolean
}
此外,接口也可以通過 extends 來擴(kuò)展類型別名定義的類型:
type?Animal?=?{
??name:?string
}
interface?Bear?extends?Animal?{
??honey:?boolean
}
同樣,類型別名也可以通過 &(交叉運(yùn)算符)來擴(kuò)展已定義的接口類型:
interface?Animal?{
??name:?string
}
type?Bear?=?Animal?&?{?
??honey:?boolean?
}
了解完 type 和 interface 的相似之處之后,接下來我們來介紹它們之間的區(qū)別。
1、類型別名可以為基本類型、聯(lián)合類型或元組類型定義別名,而接口不行
type?MyNumber?=?number;
type?StringOrNumber?=?string?|?number;
type?Point?=?[number,?number];
2、同名接口會自動合并,而類型別名不會
同名接口合并
interface?User?{
??name:?string;
}
interface?User?{
??id:?number;
}
let?user:?User?=?{?id:?666,?name:?"阿寶哥"?};
user.id;?//?666
user.name;?//?"阿寶哥"
同名類型別名會沖突
type?User?=?{
??name:?string;
};
//?標(biāo)識符“User”重復(fù)。ts(2300)
type?User?=?{ //Error
??id:?number;
};
利用同名接口自動合并的特性,在開發(fā)第三方庫的時(shí)候,我們就可以為使用者提供更好的安全保障。比如 webext-bridge 這個庫,使用 interface 定義了 ProtocolMap 接口,從而讓使用者可自由地?cái)U(kuò)展 ProtocolMap 接口。
之后,在利用該庫內(nèi)部提供的 onMessage 函數(shù)監(jiān)聽自定義消息時(shí),我們就可以推斷出不同消息對應(yīng)的消息體類型。
擴(kuò)展 ProtocolMap 接口
import?{?ProtocolWithReturn?}?from?'webext-bridge'
declare?module?'webext-bridge'?{
??export?interface?ProtocolMap?{
????foo:?{?title:?string?}
????bar:?ProtocolWithReturn
??}
}
監(jiān)聽自定義消息
import?{?onMessage?}?from?'webext-bridge'
onMessage('foo',?({?data?})?=>?{
??//?type?of?`data`?will?be?`{?title:?string?}`
??console.log(data.title)
}

如果你感興趣的話,可以看一下該項(xiàng)目的源碼。若遇到問題,可以跟阿寶哥交流。最后我們來總結(jié)一下類型別名和接口的一些使用場景。
使用類型別名的場景:
定義基本類型的別名時(shí),使用 type 定義元組類型時(shí),使用 type 定義函數(shù)類型時(shí),使用 type 定義聯(lián)合類型時(shí),使用 type 定義映射類型時(shí),使用 type
使用接口的場景:
需要利用接口自動合并特性的時(shí)候,使用 interface 定義對象類型且無需使用 type 的時(shí)候,使用 interface
掃碼查看?輕松學(xué) TypeScript?系列視頻教程

(目前已更新?12?期)
閱讀完本文,相信你已經(jīng)了解 type 和 interface 之間的區(qū)別了。你喜歡以這種形式學(xué) TS 么?喜歡的話,記得點(diǎn)贊與收藏喲。
