實(shí)現(xiàn)TypeScript中的互斥類型

前言

有這樣一個對象,它有兩個屬性:nametitle,在賦值的時候這兩個屬性只有一個能出現(xiàn),例如:name出現(xiàn)的時候title就不能出現(xiàn)备闲,title出現(xiàn)的時候name就不能出現(xiàn)。

此時捅暴,你會怎么用TypeScript來定義這個類型恬砂?本文將帶大家實(shí)現(xiàn)一個互斥類型來解決這個問題,歡迎各位感興趣的開發(fā)者閱讀本文蓬痒。

前置知識

在實(shí)現(xiàn)之前泻骤,我們需要先來了解幾個基礎(chǔ)的知識。

對象中多屬性同類型的定義

有一個對象它包含5個可選屬性a梧奢、b狱掂、cd亲轨、e符欠,他們的類型都為string,大多數(shù)人的定義方式應(yīng)該如下所示:

type obj = {
  a?:string;
  b?:string;
  c?:string;
  d?:string;
  e?:string;
}

那么瓶埋,有沒有更好的方式呢??,答案是有的诊沪,請看我的表演:

type obj = { [P in "a" | "b" | "c" | "d" | "e"]?: string };

never類型

在TypeScript中它有一個特殊的類型never养筒,它是所有類型的子類型,無法再進(jìn)行細(xì)分端姚,也就意味著除了其本身沒有類型可以再分配給它晕粪。

我們舉個例子來解釋下上述話語,如下所示:

  • 我們定義了一個變量amazing渐裸,給其賦予了never類型巫湘。
  • 我們分別給它賦了不同類型的值,全部編譯失敗,因?yàn)樗鼰o法再進(jìn)行細(xì)分了。
let amazing: never;
amazing = 12;// 報錯:amazing是never類型不能分配給number類型
amazing = true;// 報錯:amazing是never類型不能分配給boolean類型
amazing = "真神奇";// 報錯:amazing是never類型不能分配給string類型
amazing = {};// 報錯:amazing是never類型不能分配給{}類型
amazing = [];// 報錯:amazing是never類型不能分配給[]類型

剔除聯(lián)合類型中的屬性

有一組聯(lián)合類型"a" | "b" | "c" | "d"携兵,我們想剔除屬性b和c进陡,在TS中提供了一個名為Exclude的函數(shù),它可以用來做這件事蕾殴,接受兩個參數(shù):

  • UnionType 聯(lián)合類型
  • ExcludedMembers 需要進(jìn)行剔除的屬性

使用方法如下所示:

type P = Exclude<"a" | "b" | "c" | "d", "b" | "c"> // "a" | "d"

將對象中的所有屬性轉(zhuǎn)為聯(lián)合類型

有一個對象它包含2個可選屬性nametitle棒仍,我們想把它轉(zhuǎn)為聯(lián)合類型name | title讯柔,在TS中提供了一個名為keyof的函數(shù)抡蛙,他可以用來處理這個問題,使用方法如下所示:

type A =  { [P in "name" | "title"]?: string };

type UnionType = keyof A; // "name" | "title"

實(shí)現(xiàn)互斥類型

有了前置知識作為鋪墊魂迄,接下來我們就可以將其利用起來粗截,定義一個互斥類型出來,解決文章開頭所講述的問題捣炬。

接下來熊昌,我們來梳理下實(shí)現(xiàn)思路:

  • 實(shí)現(xiàn)一個排除類型,用于從A對象類型中剔除B對象類型中的屬性遥金,并將排除后的屬性類型設(shè)為never浴捆,得到一個新對象類型。
  • 基于排除類型實(shí)現(xiàn)互斥類型稿械,將A选泻、B對象類型代入排除類型中,彼此將其排除美莫,用或運(yùn)算符將二者結(jié)果連接页眯。

聰明的開發(fā)者可能已經(jīng)猜到原理了,沒錯厢呵,就是部分屬性設(shè)為never窝撵。??

實(shí)現(xiàn)代碼

接下來,我們來看下代碼的實(shí)現(xiàn)襟铭,如下所示:

// 定義排除類型:將U從T中剔除, keyof 會取出T與U的所有鍵, 限定P的取值范圍為T中的所有鍵, 并將其類型設(shè)為never
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };

// 定義互斥類型碌奉,T或U只有一個能出現(xiàn)(互相剔除時,被剔除方必須存在)
type XOR<T, U> = (Without<T, U> & U) | (Without<U, T> & T);

注意:為了類型的可復(fù)用性寒砖,我們使用了泛型赐劣,對此不熟悉的開發(fā)者請移步:TypeScript中文網(wǎng)——泛型

測試用例

我們將文章開頭所說的問題代入上述實(shí)現(xiàn)代碼中,看一下它能否將其解決??哩都,如下所示:

// A類型
type A = {
  name: string;
};

// B類型
type B = {
  title: string;
};

// A和B兩種類型只有一個能出現(xiàn)
type AOrB = XOR<A, B>;

// 傳值測試
const AOrB1: AOrB = { name: "姓名" }; // 編譯通過
const AOrB2: AOrB = { title: "標(biāo)題" }; // 編譯通過
const AOrB3: AOrB = { title: "標(biāo)題", name: "姓名" }; // 報錯: Type '{ title: string; name: string; }' is not assignable to type 'AOrB'.
const AOrB4: AOrB = { name: "姓名", otherKey: "" }; // 報錯:Type '{ name: string; otherKey: string; }' is not assignable to type 'AOrB'.

當(dāng)兩個屬性同時出現(xiàn)時魁兼,編輯器直接就拋出了類型錯誤(我們把排除后的所有屬性的類型設(shè)為了never,因此當(dāng)你給其賦任何值時它都會報類型錯誤)漠嵌,如下圖所示:

[站外圖片上傳中...(image-6c13e7-1649637918399)]

用例拆解

有一部分開發(fā)者可能對上述測試用例比較懵咐汞,把它們拆開都認(rèn)識,因?yàn)榍爸弥R里都講了儒鹿,但是寫到一起就不認(rèn)識了??化撕,沒關(guān)系,那我就把它們都拆解出來吧挺身,代碼如下所示:

type AOB = ({ name?: never } & {
      title: string;
    }) | ({ title?: never } & {
      name: string;
    });

// 傳值測試
const a: AOB = { name: "姓名" }; // 編譯通過
const b: AOB = { title: "標(biāo)題" }; // 編譯通過
const c: AOB = { title: "標(biāo)題", name: "姓名" }; // 報錯
const d: AOB = { title: "標(biāo)題", otherKey: "" }; // 報錯

看到這里侯谁,可能還有一部分開發(fā)者沒有理解,那就動起手來在編輯器里敲一敲,如果還沒理解的話墙贱,就先把這篇文章收藏热芹,日后有時間了,在拿出來學(xué)一學(xué)惨撇。

寫在最后

至此伊脓,文章就分享完畢了。

我是神奇的程序員魁衙,一位前端開發(fā)工程師报腔。

如果你對我感興趣,請移步我的個人網(wǎng)站剖淀,進(jìn)一步了解纯蛾。

  • 文中如有錯誤,歡迎在評論區(qū)指正纵隔,如果這篇文章幫到了你翻诉,歡迎點(diǎn)贊和關(guān)注??
  • 本文首發(fā)于神奇的程序員公眾號,未經(jīng)許可禁止轉(zhuǎn)載??
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捌刮,一起剝皮案震驚了整個濱河市碰煌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绅作,老刑警劉巖芦圾,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異俄认,居然都是意外死亡个少,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門眯杏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來稍算,“玉大人,你說我怎么就攤上這事役拴。” “怎么了钾埂?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵河闰,是天一觀的道長。 經(jīng)常有香客問我褥紫,道長姜性,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任髓考,我火速辦了婚禮部念,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己儡炼,他們只是感情好妓湘,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乌询,像睡著了一般榜贴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妹田,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天唬党,我揣著相機(jī)與錄音,去河邊找鬼鬼佣。 笑死驶拱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晶衷。 我是一名探鬼主播蓝纲,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼房铭!你這毒婦竟也來了驻龟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤缸匪,失蹤者是張志新(化名)和其女友劉穎翁狐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凌蔬,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡露懒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了砂心。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懈词。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辩诞,靈堂內(nèi)的尸體忽然破棺而出坎弯,到底是詐尸還是另有隱情,我是刑警寧澤译暂,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布抠忘,位于F島的核電站,受9級特大地震影響外永,放射性物質(zhì)發(fā)生泄漏崎脉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一伯顶、第九天 我趴在偏房一處隱蔽的房頂上張望囚灼。 院中可真熱鬧骆膝,春花似錦、人聲如沸灶体。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赃春。三九已至愉择,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間织中,已是汗流浹背锥涕。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狭吼,地道東北人层坠。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像刁笙,于是被迫代替她去往敵國和親破花。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容