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