React 组件中根据一个props类型去推断另一个props类型
业务场景
在实际开发中我遇到这样一个场景,有一个表单组件既可以填写国家代码,也可以填写国家区号和手机号;
- 国家代码时, 表单接收
string
类型; - 国家区号和手机号时,表单接收
string[ ]
数组分别对应区号,手机号; - 而
fieldType
定义为一个联合类型'telCode' | 'countryCode'
分别表示手机区号和国家区号表单项。
CountryCode | TelCodePhone |
---|---|
功能清楚了,就可以写props
了,最开始可能写成这个样子
typescript
interface MyCountryFieldProps {
value: string | string[]
fieldType: 'telCode' | 'countryCode'
....
}
问题
这样写会埋一个坑:
- 期望:
value
类型其实是根据fieldType
判断的,为countryCode
它应该是string
类型,telCode
才是string[]
。 - 实际: 你的类型定义告诉别人,传
value
传string
或者string[]
都可以,但很明显传错了,组件很出问题了。
解决办法
解决思想其实很简单就行告诉ts,ts帮我判断一下 countryCode
它应该是string
类型,telCode
才是string[]
这里我们用了对象的联合类型知识点:
typescript
type MyCountryFieldProps = {
fieldType: 'telCode'
value: string[]
...
} | {
fieldType: 'countryCode',
value: string
...
}
如图,ts
很清晰的告诉我们传错了值的类型,并且告诉该怎么改,这就是TS代码即注释魅力。后面再也不用担心别人用错了。
当然,组件会有其他很多prop
,写两份似乎有些笨重,那我们改一下,我们可以使用交叉类型 变化和不变的分组在用&
拼接。
typescript
type MyCountryFieldProps = {
fieldType: 'telCode'
value: string[]
} | {
fieldType: 'countryCode'
value: string
}&{
otherProps1?:string
otherProps2?:string
otherPropsFun3?:()=>void
...
}
是否可以使用泛型解决呢?
泛型:是可以理解对应为js里的函数,需要传入"类型参数",并在泛型调用时才确定具体类型,把上面联合类型改成泛型就如下。
typescript
type MyCountryFieldProps<T extends 'telCode' | 'countryCode'>={
fieldType:T,
value: T extends 'telCode' ? string[] : string;
}
tsx
type MyCountryFieldProps<T extends 'telCode' | 'countryCode'>={
fieldType:T,
value: T extends 'telCode' ? string[] : string;
}
const MyCountryField:FC<MyCountryFieldProps<typeof props.fieldType>> = ({fieldType,value}) => {
console.log(fieldType);
console.log(typeof value);
return (
<>
MyCountryField
</>
)
}
这里typeof props.fieldType
是伪代码,假设获取props.fieldType
类型值,如果可以取到确实是可以使用泛型定义的,但是笔者查了一下:
React 组件的 props 是在组件实例化之前静态确定的,而不是在运行时动态确定的。
似乎是获取不到,所以也就不能使用泛型了,但我们使用联合类型,也完全满足我们的业务场景,还是挺不错的。
总结
通过上面一个真实的业务需求,我们切实感受到ts代码即注释的魅力 。如果延伸一下的话,通过联合类型定义,我们就可以根据一个或多个props类型,去推断其他props类型定义。