TypeScript開發(fā)小狀況記錄之選且只選一個(gè)
前言
在項(xiàng)目內(nèi),使用TypeScript的過程中遇到了一些狀況、報(bào)錯(cuò)或棘手的情況,決定開一個(gè)系列記錄遇到的問題和我的解決方法。
背景
在項(xiàng)目開發(fā)中,很多時(shí)候會遇到一種場景,需要定義一個(gè)對象的類型,此類型必須包含某n個(gè)字段中的其中一種。
例如,我要定義一個(gè)工程師(Engineer)的對象,里面包括姓名(name),性別(gender),年齡(age)和一門編程語言(java/cpp/go/js四選一)的評價(jià)。
顯然,前三個(gè)字段都是很簡單的,但是第四個(gè)就有點(diǎn)麻煩了。首先,第四個(gè)字段的key是可以不一樣(甚至value也有可能不同),其次字段只能從給定的里面4選1。
初步方案
一開始是考慮使用可選或聯(lián)合類型,但是發(fā)現(xiàn)沒有辦法進(jìn)行4選1的限制,對于沒有編程語言字段,或者多個(gè)編程語言字段的情況并沒有很好的限制。最后只能使用泛型,再使用時(shí)進(jìn)行顯式的聲明。
于是,類型定義如下:
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>
對該聲明的校驗(yàn)代碼如下:
// 正確 const candidate: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, java: 'fabulous' } // 錯(cuò)誤,聲明了java,但是卻同時(shí)定義了java和go字段 const candidate_1: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, java: 'fabulous', go: 'not bad' } // 錯(cuò)誤,聲明了java,但是類型不正確 const candidate_2: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, java: 666 } // 錯(cuò)誤,聲明了java,但是卻定義了go字段 const candidate_3: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, go: 'not bad' } // 錯(cuò)誤,聲明了java,但是卻同時(shí)定義了cpp和go字段 const candidate_4: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22, cpp: 'unknown', go: 'not bad' } // 錯(cuò)誤,聲明了java,但是卻沒有定義java字段 const candidate_5: Engineer<'java'> = { name: 'Jack', gender: 'male', age: 22 } // 錯(cuò)誤,聲明了ICodingLangRating中不存在的python const candidate_6: Engineer<'python'> = { name: 'Jack', gender: 'male', age: 22, python: 'just so so' }
從校驗(yàn)代碼可以看出,針對各種不符合期望的情況:
- 聲明a,卻定義a和b
- 聲明a,但定義a的類型不正確
- 聲明a,卻定義b
- 聲明a,卻定義b和c
- 聲明a,卻沒有定義a
- 聲明不合法的f
都能做出正確的限制,確保在業(yè)務(wù)場景的代碼中,有且只有一個(gè)合法范圍的字段。
但是,轉(zhuǎn)折來了!
在后來的使用中,我們發(fā)現(xiàn),其實(shí)這個(gè)解決方案只是一個(gè)弱限制,如果在泛型的顯式聲明中,傳入聯(lián)合類型的話,那還是可以繞過有且只有一個(gè)編程語言字段的限制。
// 正確,聲明了java和go,并同時(shí)定義了java和go字段 const candidate_1: Engineer<'java' | 'go'> = { name: 'Jack', gender: 'male', age: 22, java: 'fabulous', go: 'not bad' }
難道就真的沒有辦法做到只能選擇一個(gè)的限制么?
終極方案
根據(jù)上面的嘗試,目前我們還缺少的是如何阻止同時(shí)有2個(gè)或以上的合法字段出現(xiàn)。最笨的方式就是為每一個(gè)語言都定義一個(gè)類似{ langName: string }
這樣的類型然后通過extends或者聯(lián)合類型使用,但是顯然這樣就沒有辦法做到在其它情況通用。
而通過官方現(xiàn)成的工具類型,由于都是支持字面量和聯(lián)合類型,沒有辦法篩選出只包含一個(gè)字段的類型。就在這時(shí),我想到,是不是可以定義出一個(gè)類型,包含全部字段,但是只有一個(gè)字段是正確有意義,其他字段都是無意義的呢。
最終,我就構(gòu)造出下面這個(gè)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' } // 錯(cuò)誤 const lang2: OneLang = { python: 'unknown' } // 錯(cuò)誤 const lang3: OneLang = { java: 'good', go: 'good' } // 錯(cuò)誤 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>
使用了這個(gè)PickOne工具類型,我不需要在使用的時(shí)候顯式的指定編程語言,甚至還能在其它類似的場景使用。
總結(jié)
到此這篇關(guān)于TypeScript開發(fā)小狀況記錄之選且只選一個(gè)的文章就介紹到這了,更多相關(guān)TypeScript選且只選一個(gè)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript使用Replace進(jìn)行字符串替換的方法
這篇文章主要介紹了JavaScript使用Replace進(jìn)行字符串替換的方法,涉及Replace進(jìn)行一次替換與全部替換的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04uniapp使用navigateBack方法返回上級頁面并刷新的簡單示例
最近寫uniapp項(xiàng)目的時(shí)候發(fā)現(xiàn)有時(shí)候需要更新頁面數(shù)據(jù),所以下面這篇文章主要給大家介紹了關(guān)于uniapp使用navigateBack方法返回上級頁面并刷新的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03js實(shí)現(xiàn)簡單隨機(jī)抽獎(jiǎng)的方法
這篇文章主要介紹了js實(shí)現(xiàn)簡單隨機(jī)抽獎(jiǎng)的方法,涉及字符串的操作、setInterval定時(shí)調(diào)用等技巧,需要的朋友可以參考下2015-01-01在mpvue框架中使用Vant WeappUI組件庫的注意事項(xiàng)【推進(jìn)】
這篇文章主要介紹了在mpvue框架中使用Vant WeappUI組件庫的注意事項(xiàng),本文給大家提到了引入組件庫的兩種方法,需要的朋友可以參考下2019-06-06echarts圖形x、y坐標(biāo)文字設(shè)置間隔顯示及相關(guān)問題詳解
最近在做一個(gè)web的數(shù)據(jù)統(tǒng)計(jì)部分用到了Echart,下面這篇文章主要給大家介紹了關(guān)于echarts圖形x、y坐標(biāo)文字設(shè)置間隔顯示及相關(guān)問題的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08JS腳本加載后執(zhí)行相應(yīng)回調(diào)函數(shù)的操作方法
本文主要講解怎么在成功加載 js 文件后再執(zhí)行相應(yīng)回調(diào)任務(wù),對JS腳本加載后執(zhí)行相應(yīng)回調(diào)函數(shù)的操作方法感興趣的朋友,通過本文學(xué)習(xí)下吧2018-02-02