溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

TypeScript開發(fā)中選且只選一個(gè)問題怎么解決

發(fā)布時(shí)間:2022-10-09 17:30:06 來源:億速云 閱讀:151 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了TypeScript開發(fā)中選且只選一個(gè)問題怎么解決的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇TypeScript開發(fā)中選且只選一個(gè)問題怎么解決文章都會(huì)有所收獲,下面我們一起來看看吧。

背景

在項(xiàng)目開發(fā)中,很多時(shí)候會(huì)遇到一種場景,需要定義一個(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è)編程語言字段的限制。

TypeScript開發(fā)中選且只選一個(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í)候顯式的指定編程語言,甚至還能在其它類似的場景使用。

關(guān)于“TypeScript開發(fā)中選且只選一個(gè)問題怎么解決”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“TypeScript開發(fā)中選且只選一個(gè)問題怎么解決”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI