背景
在使用React的开发项目时,大多数关于ts的类型定义问题,可以在备忘单里面找到;但是当遇到数据类型为数组,对象的时候,为了方便,使用any来进行定义,或者直接用空数据来初始化,当项目变得庞大时,一个组件可能会包括20多个any,使得不容易维护。
由于any会完全失去类型判断,其实是比较危险的,使用any就相当于放弃了类型检测,也就基本上放弃了typescript。
规范化TS的写法,将更有利用项目更好地维护,提高代码的可读性。
使用any较多的场景
定义变量时,变量的数据来源于接口,而接口数量多,定义起来稍显繁杂,所以不进行定义,使用any;
由于变量定义为any,使用该变量进行处理时,也只能使用any;
使用到antd等一些库,遇到函数中的event等参数,不知道如何去定义其类型,直接使用any;
不规范使用Ts举例:
// bad
const [templateTableList,setTemplateTableList] = useState<any>([])
//bad
const [templateTableList,setTemplateTableList] = useState([])
// bad
const temp: any = templateTableList.filter((item:any) => {
return item.identifier === value;
});
// bad
const onChangeExist = (e: any) => {
setIsExist(e.target.value);
};
知识补充:TS类型推断
基本类型推断
Ts会进行基本类型的类型的推断,如:
const [val, toggle] = React.useState(false);
等同于:
const [val, toggle] = React.useState<boolean>(false);
复杂类型推断
但是对于数组或者对象,当无法推断一个变量时发出一个错误(或者只能推断为一个隐式的 any 类型)时,我们可以:
- 通过显式添加 :any 的类型注解,来让它成为一个 any 类型;
const onChangeExist = (e: any) => {
setIsExist(e.target.value);
};
- 通过一些更正确的类型注解来帮助 TypeScript 推断类型。
import type { RadioChangeEvent } from 'antd/lib/radio';
const onChangeExist = (e: RadioChangeEvent) => {
setIsExist(e.target.value);
};
显然,后者更符合我们的开发习惯。
解决any出现次数过次的问题
复杂类型的类型定义
- 细拆出重复定义的公共项,使用extents 关键字或者 & 交叉运算符来进行整合,提高利用率;
例1:
// bad
interface Person {
firstName: string;
lastName: string;
}
interface PersonWithBirthDate {
firstName: string;
lastName: string;
birth: Date;
}
// good
interface Person {
firstName: string;
lastName: string;
}
interface PersonWithBirthDate extends Person {
birth: Date;
}
例2:
export type List = {
creatTime: number | string;
creator: string;
desc: string;
id: string;
modifier: string;
token: string;
updateTime: number | string;
};
export type ProjectList = {
id: string;
projectName: string;
[key: string]: any;
} & List;
export type TaskList = {
name: string;
[key: string]: any;
} & List;
新的问题:如果很多字段要打问号怎么办?下面会讲到
- 使用typeof定义一个类型匹配初始值(常见的使用场景之一:固定的Schema配置适用)
const INIT_OPTIONS= {
width: 640,
height: 480,
color: "#00FF00",
label: "VGA",
};
interface Options {
width: number;
height: number;
color: string;
label: string;
}
// 快速获取配置对象的形状
type Options = typeof INIT_OPTIONS;
- 使用Ts 内置类型来解决?号太多的问题
- Utility Types
- 充分利用lib.es5.d.ts中的Partial, Pick , Extract, Omit等方法,扩展第三方、或已存在的类型,不要重复定义完全一样的字段,详细的内容请戳。
使用IDEA智能联想推断类型:以antd为例
antd里面事件参数event的定义
例如: 在RadioGroup组件中,绑定一个onChange事件,如下:
<Radio.Group onChange={onChangeExist} value={isExist}>
<Radio value="Exist">Exist</Radio>
<Radio value="NotExist">Not Exist</Radio>
</Radio.Group>
这种情况下,需要层层深入去查找RadioGroup的实现才能知道e是如何定义的,比较繁杂
const onChangeExist = (e: any) => {
setIsExist(e.target.value);
};
一个比较简便的方法:
步骤1:将事件的写法修改为:
<Radio.Group onChange={(e)=>{onChangeExist(e)} value={isExist}>
<Radio value="Exist">Exist</Radio>
<Radio value="NotExist">Not Exist</Radio>
</Radio.Group>
步骤2:将鼠标放到参数 e 上去,可以显示出参数的类型
步骤3:引入类开,修改any类型为:RadioChangeEvent ,同时恢复onChange的写法:
<Radio.Group onChange={onChangeExist} value={isExist}>
<Radio value="Exist">Exist</Radio>
<Radio value="NotExist">Not Exist</Radio>
</Radio.Group>
import type { RadioChangeEvent } from 'antd/lib/radio';
const onChangeExist = (e: RadioChangeEvent) => {
setIsExist(e.target.value);
};