大家好,我是若川。持續組織了8個月源碼共讀活動,感興趣的可以?點此加我微信ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列。另外:目前建有江西|湖南|湖北
籍前端群,可加我微信進群。
阿寶哥精心準備的《輕松學 TypeScript》?視頻教程已經更新到第十二期了,通過形象生動的動畫,讓你輕松搞懂 TypeScript 的難點和核心知識點!
如果你簡歷上的技能有寫 TypeScript,那么面試官可能會問你 type 和 interface 之間有什么區別?你知道怎么回答這個問題么?如果不知道的話,那看完本文也許你就懂了。
類型別名 type 可以用來給一個類型起個新名字,當命名基本類型或聯合類型等非對象類型時非常有用:
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<T>?=?{[P?in?keyof?T]?:?T[P];
};type?Required<T>?=?{[P?in?keyof?T]-?:?T[P];
};type?Pick<T,?K?extends?keyof?T>?=?{[P?in?K]:?T[P];
};type?Record<K?extends?keyof?any,?T>?=?{[P?in?K]:?T;
};type?Exclude<T,?U>?=?T?extends?U???never?:?T;
而接口 interface 只能用于定義對象類型,Vue 3 中的 App 對象就是使用 interface 來定義的:
//?packages/runtime-core/src/apiCreateApp.ts
export?interface?App<HostElement?=?any>?{version:?stringconfig:?AppConfiguse(plugin:?Plugin,?...options:?any[]):?thismixin(mixin:?ComponentOptions):?thiscomponent(name:?string):?Component?|?undefined?//?Gettercomponent(name:?string,?component:?Component):?this?//?Setterdirective(name:?string):?Directive?|?undefineddirective(name:?string,?directive:?Directive):?this
}
由以上代碼可知,在定義接口時,我們可以同時聲明對象類型上的屬性和方法。了解 type 和 interface 的作用之后,我們先來介紹一下它們的相似之處。
1、類型別名和接口都可以用來描述對象或函數
類型別名
type?Point?=?{x:?number;y:?number;
};type?SetPoint?=?(x:?number,?y:?number)?=>?void;
在以上代碼中,我們通過 type 關鍵字為對象字面量類型和函數類型分別取了一個別名,從而方便在其他地方使用這些類型。
接口
interface?Point?{x:?number;y:?number;
}interface?SetPoint?{(x:?number,?y:?number):?void;
}
2、類型別名和接口都支持擴展
類型別名通過 &(交叉運算符)來擴展,而接口通過 extends 的方式來擴展。
類型別名擴展
type?Animal?=?{name:?string
}type?Bear?=?Animal?&?{?honey:?boolean?
}const?bear:?Bear?=?getBear()?
bear.name
bear.honey
接口擴展
interface?Animal?{name:?string
}interface?Bear?extends?Animal?{honey:?boolean
}
此外,接口也可以通過 extends 來擴展類型別名定義的類型:
type?Animal?=?{name:?string
}interface?Bear?extends?Animal?{honey:?boolean
}
同樣,類型別名也可以通過 &(交叉運算符)來擴展已定義的接口類型:
interface?Animal?{name:?string
}type?Bear?=?Animal?&?{?honey:?boolean?
}
了解完 type 和 interface 的相似之處之后,接下來我們來介紹它們之間的區別。
1、類型別名可以為基本類型、聯合類型或元組類型定義別名,而接口不行
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;
};//?標識符“User”重復。ts(2300)
type?User?=?{ //Errorid:?number;
};
利用同名接口自動合并的特性,在開發第三方庫的時候,我們就可以為使用者提供更好的安全保障。比如 webext-bridge 這個庫,使用 interface 定義了 ProtocolMap 接口,從而讓使用者可自由地擴展 ProtocolMap 接口。
之后,在利用該庫內部提供的 onMessage
函數監聽自定義消息時,我們就可以推斷出不同消息對應的消息體類型。
擴展 ProtocolMap 接口
import?{?ProtocolWithReturn?}?from?'webext-bridge'declare?module?'webext-bridge'?{export?interface?ProtocolMap?{foo:?{?title:?string?}bar:?ProtocolWithReturn<CustomDataType,?CustomReturnType>}
}
監聽自定義消息
import?{?onMessage?}?from?'webext-bridge'onMessage('foo',?({?data?})?=>?{//?type?of?`data`?will?be?`{?title:?string?}`console.log(data.title)
}
如果你感興趣的話,可以看一下該項目的源碼。若遇到問題,可以跟阿寶哥交流。最后我們來總結一下類型別名和接口的一些使用場景。
使用類型別名的場景:
定義基本類型的別名時,使用 type
定義元組類型時,使用 type
定義函數類型時,使用 type
定義聯合類型時,使用 type
定義映射類型時,使用 type
使用接口的場景:
需要利用接口自動合并特性的時候,使用 interface
定義對象類型且無需使用 type 的時候,使用 interface
·················?若川簡介?·················
你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經堅持寫了8年,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助4000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。
掃碼加我微信 ruochuan02、拉你進源碼共讀群
今日話題
目前建有江西|湖南|湖北?籍 前端群,想進群的可以加我微信 ruochuan12?進群。分享、收藏、點贊、在看我的文章就是對我最大的支持~