实际开发中TypeScript 使用记录

353 阅读2分钟

React 组件中根据一个props类型去推断另一个props类型

业务场景

在实际开发中我遇到这样一个场景,有一个表单组件既可以填写国家代码,也可以填写国家区号和手机号;

  • 国家代码时, 表单接收string 类型;
  • 国家区号和手机号时,表单接收 string[ ]数组分别对应区号,手机号;
  • fieldType 定义为一个联合类型 'telCode' | 'countryCode' 分别表示手机区号和国家区号表单项。
CountryCodeTelCodePhone
image.pngimage.png

功能清楚了,就可以写props了,最开始可能写成这个样子

  interface  MyCountryFieldProps {
      value: string | string[]
      fieldType: 'telCode' | 'countryCode'
      ....
  }

问题

这样写会埋一个坑:

  • 期望: value类型其实是根据fieldType 判断的,为countryCode它应该是string类型,telCode才是string[]
  • 实际: 你的类型定义告诉别人,传valuestring 或者 string[]都可以,但很明显传错了,组件很出问题了。

image.png

解决办法

解决思想其实很简单就行告诉ts,ts帮我判断一下 countryCode它应该是string类型,telCode才是string[]

这里我们用了对象的联合类型知识点:

type MyCountryFieldProps = {
    fieldType: 'telCode'
    value: string[]
    ...
} | {
    fieldType:  'countryCode',
    value: string
    ...
}

如图,ts很清晰的告诉我们传错了值的类型,并且告诉该怎么改,这就是TS代码即注释魅力。后面再也不用担心别人用错了。

20240228234259_rec_.gif

当然,组件会有其他很多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类型定义。