TypeScript选且只选一个问题怎么解决(typescript,开发技术)

时间:2024-05-02 09:02:59 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

背景

在项目开发中,很多时候会遇到一种场景,需要定义一个对象的类型,此类型必须包含某n个字段中的其中一种。

例如,我要定义一个工程师(Engineer)的对象,里面包括姓名(name),性别(gender),年龄(age)和一门编程语言(java/cpp/go/js四选一)的评价。

显然,前三个字段都是很简单的,但是第四个就有点麻烦了。首先,第四个字段的key是可以不一样(甚至value也有可能不同),其次字段只能从给定的里面4选1。

初步方案

一开始是考虑使用可选或联合类型,但是发现没有办法进行4选1的限制,对于没有编程语言字段,或者多个编程语言字段的情况并没有很好的限制。最后只能使用泛型,再使用时进行显式的声明。

于是,类型定义如下:

interfaceICodingLangRating{
java:string
cpp:string
go:string
js:string
}

typeEngineer<KextendskeyofICodingLangRating>={
name:string
gender:'male'|'female'
age:number
}&Pick<ICodingLangRating,K>

对该声明的校验代码如下:

//正确
constcandidate:Engineer<'java'>={
name:'Jack',
gender:'male',
age:22,
java:'fabulous'
}

//错误,声明了java,但是却同时定义了java和go字段
constcandidate_1:Engineer<'java'>={
name:'Jack',
gender:'male',
age:22,
java:'fabulous',
go:'notbad'
}

//错误,声明了java,但是类型不正确
constcandidate_2:Engineer<'java'>={
name:'Jack',
gender:'male',
age:22,
java:666
}

//错误,声明了java,但是却定义了go字段
constcandidate_3:Engineer<'java'>={
name:'Jack',
gender:'male',
age:22,
go:'notbad'
}

//错误,声明了java,但是却同时定义了cpp和go字段
constcandidate_4:Engineer<'java'>={
name:'Jack',
gender:'male',
age:22,
cpp:'unknown',
go:'notbad'
}

//错误,声明了java,但是却没有定义java字段
constcandidate_5:Engineer<'java'>={
name:'Jack',
gender:'male',
age:22
}

//错误,声明了ICodingLangRating中不存在的python
constcandidate_6:Engineer<'python'>={
name:'Jack',
gender:'male',
age:22,
python:'justsoso'
}

从校验代码可以看出,针对各种不符合期望的情况:

  • 声明a,却定义a和b

  • 声明a,但定义a的类型不正确

  • 声明a,却定义b

  • 声明a,却定义b和c

  • 声明a,却没有定义a

  • 声明不合法的f

都能做出正确的限制,确保在业务场景的代码中,有且只有一个合法范围的字段。

但是,转折来了!

在后来的使用中,我们发现,其实这个解决方案只是一个弱限制,如果在泛型的显式声明中,传入联合类型的话,那还是可以绕过有且只有一个编程语言字段的限制。

TypeScript选且只选一个问题怎么解决

//正确,声明了java和go,并同时定义了java和go字段
constcandidate_1:Engineer<'java'|'go'>={
name:'Jack',
gender:'male',
age:22,
java:'fabulous',
go:'notbad'
}

难道就真的没有办法做到只能选择一个的限制么?

终极方案

根据上面的尝试,目前我们还缺少的是如何阻止同时有2个或以上的合法字段出现。最笨的方式就是为每一个语言都定义一个类似{ langName: string }这样的类型然后通过extends或者联合类型使用,但是显然这样就没有办法做到在其它情况通用。

而通过官方现成的工具类型,由于都是支持字面量和联合类型,没有办法筛选出只包含一个字段的类型。就在这时,我想到,是不是可以定义出一个类型,包含全部字段,但是只有一个字段是正确有意义,其他字段都是无意义的呢。

最终,我就构造出下面这个PickOne工具类型:

typePickOne<T>={

}[keyofT]

测试代码如下:

typeOneLang=PickOne<ICodingLangRating>

//正确
constlang:OneLang={
java:'good'
}

//错误
constlang2:OneLang={
python:'unknown'
}

//错误
constlang3:OneLang={
java:'good',
go:'good'
}

//错误
constlang4:OneLang={
java:123
}

最后,类型定义代码如下:

interfaceICodingLangRating{
java:string
cpp:string
go:string
js:string
}

typeEngineer={
name:string
gender:'male'|'female'
age:number
}&PickOne<ICodingLangRating>

使用了这个PickOne工具类型,我不需要在使用的时候显式的指定编程语言,甚至还能在其它类似的场景使用。

本文:TypeScript选且只选一个问题怎么解决的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:基于angular material theming机制如何修改mat-toolbar的背景色下一篇:

9 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18