TypeScript實(shí)用技巧?Nominal?Typing名義類型詳解
Nominal Typing(名義類型)
概念解析
意思是給一個(gè)類型附加上一個(gè)“名義”,從而防止結(jié)構(gòu)類型在某些情況下由于類型結(jié)構(gòu)相似而被錯(cuò)用。假設(shè)有如下代碼:
interface Vector2D { x: number, y: number };
interface Vector3D { x: number, y: number, z: number };
function calc(vector: Vector2D): void;
const vector: Vector3D = { x: 1, y: 1, z: 1}
calc(vector) // 并沒有拋出錯(cuò)誤
看上去calc()函數(shù)應(yīng)該只能傳入Vector2D類型,但其實(shí)也可以傳入Vector3D,因?yàn)楸举|(zhì)上Vector3D是Vector2D的子集。對(duì)于calc()函數(shù)來說,傳入的vector變量的類型中只要同時(shí)具有x、y屬性即可通過類型校驗(yàn)。
這種特性這在TS中被稱為 Structual Typing(結(jié)構(gòu)類型)。通常來說這會(huì)給我們的編碼過程帶來便利,但極端情況下,也可能不符合我們的預(yù)期。
假如嚴(yán)格規(guī)定函數(shù)只能傳入Vector2D類型而不能傳入Vector3D類型,那么在類型實(shí)現(xiàn)上,就可以使用 名義上的類型(Nominal Type),通過為原類型添加一個(gè)獨(dú)有標(biāo)識(shí)來區(qū)分彼此:
interface Vector2D { x: number, y: number, __type: '2d' };
interface Vector3D { x: number, y: number, z: number, __type: '3d' };
function calc(vector: Vector2D): void;
對(duì)于interface,我們可以直接為其增加標(biāo)志屬性,但 primitive types (原始類型) 要如何處理呢?答案是使用交叉類型,例如:
type Food = string & { _type: 'food' };
type Money = number & { _type: 'money' }
你可能會(huì)對(duì)最終類型有所疑問,但這樣處理之后,他們依舊是原始類型。因?yàn)閷?shí)際上原始類型最終都會(huì)解析成對(duì)應(yīng)的 WrapperType (包裹類型),例如string → String,number → Number,就像在JS中一樣。這意味著你可把它們當(dāng)做原始類型使用:
const money = 100 as Money; const bill = money * 1; // bill 仍然是 number 類型
雖然這樣的使用方式顯得不太優(yōu)雅,甚至有些繁瑣,但在某些情況下至少可以保證類型安全。假如你的類型系統(tǒng)中有許多 基礎(chǔ)類型單元,那可能會(huì)非常有用。
拓展應(yīng)用
這樣的類型雖然可以被當(dāng)做原始類型使用,但本質(zhì)上又不是純粹的原始類型。我們可以利用這個(gè)特性,寫出一些非常有趣和實(shí)用的類型。
例如在字面量枚舉時(shí),我們可以在限制預(yù)設(shè)值的同時(shí),使用 基于原始類型拓展出來的名義類型 進(jìn)行兜底,從而使得我們的類型能夠在具備足夠自由性的前提下,仍能享受到TypeScript的類型提示,如下:

可以看到,CustomLiteral名義類型不但可以享受到Literal字面量的類型提示,又能跳出枚舉的限制,使用自定義字符串。倘若我們將Literal和原始類型string直接交叉:

聯(lián)合類型的機(jī)制本質(zhì)上是求并集,而求并集最終得到的類型將會(huì)是更加廣泛而通用的string,這使得我們反而使失去了字面量類型的推導(dǎo)。想要實(shí)現(xiàn)上述效果,就需要為string類型賦予“名義”,使它不同于普通的原始類型,不再那么“廣泛”,從而在求并集的時(shí)候,不至于被string類型徹底拿捏。附上Typescript Playground。
在Vue中的應(yīng)用
其實(shí)在Vue3源碼中也有很多 Nominal Typing 的例子,例如VNode、Teleport、KeepAlive、Fragment等等這些內(nèi)置組件,他們的定義中都有一個(gè)標(biāo)志變量用于區(qū)分。分別對(duì)應(yīng)了__is_VNode,__isTeleport,__isKeepAlive,__isFragment。下圖是KeepAlive組件的聲明,更多組件聲明可以移步官方倉庫查閱。

如果類型聲明的位置處在函數(shù)入?yún)⑸?,為了防止與用戶定義的屬性產(chǎn)生沖突,通常會(huì)采用unique symbol作為鍵值來構(gòu)造名義類型,例如:

以上就是TypeScript實(shí)用技巧 Nominal Typing名義類型詳解的詳細(xì)內(nèi)容,更多關(guān)于TypeScript名義類型Nominal Typing的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解析Javascript設(shè)計(jì)模式Revealing?Module?揭示模式單例模式
這篇文章主要為大家解析了Javascript設(shè)計(jì)模式Revealing?Module?揭示模式及Singleton單例模式示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
JavaScript實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列
這篇文章主要介紹了JavaScript如何實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列,在計(jì)算機(jī)里,隊(duì)列是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。就跟我們平時(shí)排隊(duì)一樣,先到的排在前面,前面的優(yōu)先處理,下面我們就來看看在JavaScript里面的優(yōu)先隊(duì)列又當(dāng)如何2021-12-12
微信小程序 藍(lán)牙的實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了微信小程序 藍(lán)牙的實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-06-06
javascript的setTimeout()使用方法總結(jié)
這篇文章主要給大家分享javascript的setTimeout()使用方法總結(jié),js的setTimeout方法用處比較多,通常用在頁面刷新了、延遲執(zhí)行了等等,下面我們一起來看看文章對(duì)該內(nèi)容的具體總結(jié)吧,需要的朋友可以參考一下2021-11-11
JavaScript自動(dòng)化測試添加頁面DOM元素唯一ID方案示例
這篇文章主要為大家介紹了JavaScript自動(dòng)化測試添加頁面DOM元素唯一ID方案示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
8個(gè)JS的reduce使用實(shí)例和reduce操作方式
reduce方法是JavaScript中一個(gè)比較強(qiáng)大的方法,可能在平時(shí)開發(fā)中,有人根本沒用過,通過下面的8個(gè)例子,學(xué)會(huì)reduce的用法以及它的常用場景,需要的朋友可以參考一下2021-09-09
跨端開發(fā)框架avm組件封裝經(jīng)驗(yàn)分享
這篇文章主要為大家介紹了跨端開發(fā)框架avm組件封裝的經(jīng)驗(yàn)分享,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
詳解微信小程序如何實(shí)現(xiàn)類似ChatGPT的流式傳輸
這篇文章主要為大家介紹了微信小程序如何實(shí)現(xiàn)類似ChatGPT的流式傳輸示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
微信小程序上滑加載下拉刷新(onscrollLower)分批加載數(shù)據(jù)(一)
這篇文章主要介紹了微信小程序上滑加載下拉刷新(onscrollLower)分批加載數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2017-05-05

