这篇文章主要讲解了“TypeScript选且只选一个问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“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'
}
从校验代码可以看出,针对各种不符合期望的情况:
声明a,却定义a和b
声明a,但定义a的类型不正确
声明a,却定义b
声明a,却定义b和c
声明a,却没有定义a
声明不合法的f
都能做出正确的限制,确保在业务场景的代码中,有且只有一个合法范围的字段。
但是,转折来了!
在后来的使用中,我们发现,其实这个解决方案只是一个弱限制,如果在泛型的显式声明中,传入联合类型的话,那还是可以绕过有且只有一个编程语言字段的限制。
// 正确,声明了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选且只选一个问题怎么解决这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://www.zhuxianfei.com/jishu/js/59357.html