<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在專案內,使用TypeScript的過程中遇到了一些狀況、報錯或棘手的情況,決定開一個系列記錄遇到的問題和我的解決方法。
在專案開發中,很多時候會遇到一種場景,需要定義一個物件的型別,此型別必須包含某n個欄位中的其中一種。
例如,我要定義一個工程師(Engineer)的物件,裡面包括姓名(name),性別(gender),年齡(age)和一門程式語言(java/cpp/go/js四選一)的評價。
顯然,前三個欄位都是很簡單的,但是第四個就有點麻煩了。首先,第四個欄位的key是可以不一樣(甚至value也有可能不同),其次欄位只能從給定的裡面4選1。
一開始是考慮使用可選或聯合型別,但是發現沒有辦法進行4選1的限制,對於沒有程式語言欄位,或者多個程式語言欄位的情況並沒有很好的限制。最後只能使用泛型,再使用時進行顯式的宣告。
於是,型別定義如下:
interface ICodingLangRating { java: string cpp: string go: string js: string } type Engineer<K extends keyof ICodingLangRating> = { name: string gender: 'male' | 'female' age: number } & Pick<ICodingLangRating, K>
對該宣告的校驗程式碼如下:
// 正確 const candidate: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, java: 'fabulous' } // 錯誤,宣告了java,但是卻同時定義了java和go欄位 const candidate_1: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, java: 'fabulous', go: 'not bad' } // 錯誤,宣告了java,但是型別不正確 const candidate_2: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, java: 666 } // 錯誤,宣告了java,但是卻定義了go欄位 const candidate_3: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, go: 'not bad' } // 錯誤,宣告了java,但是卻同時定義了cpp和go欄位 const candidate_4: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, cpp: 'unknown', go: 'not bad' } // 錯誤,宣告了java,但是卻沒有定義java欄位 const candidate_5: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22 } // 錯誤,宣告了ICodingLangRating中不存在的python const candidate_6: Engineer<'python'> = { name: 'Jack', gender: 'male', age: 22, python: 'just so so' }
從校驗程式碼可以看出,針對各種不符合期望的情況:
都能做出正確的限制,確保在業務場景的程式碼中,有且只有一個合法範圍的欄位。
但是,轉折來了!
在後來的使用中,我們發現,其實這個解決方案只是一個弱限制,如果在泛型的顯式宣告中,傳入聯合型別的話,那還是可以繞過有且只有一個程式語言欄位的限制。
// 正確,宣告了java和go,並同時定義了java和go欄位 const candidate_1: Engineer<'java' | 'go'> = { name: 'Jack', gender: 'male', age: 22, java: 'fabulous', go: 'not bad' }
難道就真的沒有辦法做到只能選擇一個的限制麼?
根據上面的嘗試,目前我們還缺少的是如何阻止同時有2個或以上的合法欄位出現。最笨的方式就是為每一個語言都定義一個類似{ langName: string }
這樣的型別然後通過extends或者聯合型別使用,但是顯然這樣就沒有辦法做到在其它情況通用。
而通過官方現成的工具型別,由於都是支援字面量和聯合型別,沒有辦法篩選出只包含一個欄位的型別。就在這時,我想到,是不是可以定義出一個型別,包含全部欄位,但是隻有一個欄位是正確有意義,其他欄位都是無意義的呢。
最終,我就構造出下面這個PickOne工具型別:
type PickOne<T> = { [K in keyof T]: Record<K, T[K]> & Partial<Record<Exclude<keyof T, K>, undefined>> }[keyof T]
測試程式碼如下:
type OneLang = PickOne<ICodingLangRating> // 正確 const lang: OneLang = { java: 'good' } // 錯誤 const lang2: OneLang = { python: 'unknown' } // 錯誤 const lang3: OneLang = { java: 'good', go: 'good' } // 錯誤 const lang4: OneLang = { java: 123 }
最後,型別定義程式碼如下:
interface ICodingLangRating { java: string cpp: string go: string js: string } type Engineer = { name: string gender: 'male' | 'female' age: number } & PickOne<ICodingLangRating>
使用了這個PickOne工具型別,我不需要在使用的時候顯式的指定程式語言,甚至還能在其它類似的場景使用。
到此這篇關於TypeScript開發小狀況記錄之選且只選一個的文章就介紹到這了,更多相關TypeScript選且只選一個內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45