TypeScript 中几个小技巧
程序员文章站
2024-02-19 18:11:28
...
泛型的用法
本来以为在开发中基本用不到泛型,没想到今天还是碰到了。公司的项目是用 React 写的,其中需要使用 useSelector
的 hook 从 redux 获取数据,代码如下:
const { auditFilterOptions, auditRecords, auditTableLoading } = useSelector(state => {
auditFilterOptions: state.getIn(["resource", "auditFilterOptions"]),
auditRecords: state.getIn(["resource", "auditRecords"]),
auditTableLoading: state.getIn(["auditTableLoading ", "auditTableLoading "])
})
在使用 TypeScript 的时候,提示需要给形参 state
声明类型。但是我们知道,state
是一个对象,上面挂载了 getIn
方法。因此,在声明类型的时候,势必要声明 getIn
方法的入参和返回值类型。但是在上面代码中,每一个 store 的字段,对应的数据类型是不一样的,这导致每次调用 getIn
方法的返回值可类型可能是不一样的:
type AuditFilterOptions = {
limitMinTime: string;
operateUsers: string[];
serviceTypes: string[];
tenants: string[];
}
type AuditRecords = {
total: number;
page: number;
rows: {
operateTime: string;
operateMessage: string;
operateUser: string;
operateType: string;
}[];
}
type AuditTableLoading = boolean;
之前同事在开发的时候,直接简单粗暴:
interface State {
getIn: Function;
}
这样操作,虽然类型检查不会报错,但是无疑丢失了类型信息,返回类型直接被推导为 any
,无法获取到对象属性的类型了。那么需要怎么样才可以保留类型信息呢?
之前在 Java 里面用过函数重载,就是一个函数名可以有不同的类型定义,编译器通过不同的入参及入参类型调用对应的函数。在 TS 中也提供了函数重载机制:
interface State {
getIn(a: string[]): AuditFilterOptions;
getIn(a: string[]): AuditRecords;
getIn(a: string[]): AuditTableLoading;
}
但是实际测试发现,由于入参都一样,编译器没办法区分类型,始终匹配到的是第一个返回值类型。
那么怎么样才能实现匹配不同类型的返回值呢?想到了泛型。泛型就是参数化类型,可以传入类型参数。按照这个思路,state
的类型可以这样声明:
interface State {
getIn<T>(a: string[]): T;
}
对应 useSelector
就可以这样使用:
const { auditFilterOptions, auditRecords, auditTableLoading } = useSelector((state: State) => {
auditFilterOptions: state.getIn<AuditFilterOptions>(["resource", "auditFilterOptions"]),
auditRecords: state.getIn<AuditRecords>(["resource", "auditRecords"]),
auditTableLoading: state.getIn<AuditTableLoading>(["auditTableLoading ", "auditTableLoading "])
})
然后也尝试过这个方案:
interface State<T> {
getIn(a: string[]): T;
}
但这样的话,useSelector
就得拆开写了,太麻烦,还是上面的写法简单:
const { auditFilterOptions } = useSelector((state: State<AuditFilterOptions>) => {
auditFilterOptions: state.getIn(["resource", "auditFilterOptions"])
})
const { auditRecords } = useSelector((state: State<AuditRecords>) => {
auditRecords: state.getIn(["resource", "auditRecords"]),
})
const { auditTableLoading } = useSelector((state: State<AuditTableLoading>) => {
auditTableLoading: state.getIn(["auditTableLoading ", "auditTableLoading "])
})
类型断言
在定义类型的时候,会遇到属性可能不存在的情况:
type Services = {
metrics?: {
cpuUsage: number;
memoryUsage: number;
}
}
那么在访问可能不存在的属性的时候,编译器会报错,提示该属性可能为 undefined :
const memoryUsage = Math.floor(pod.metrics?.memoryUsage /1024 / 1024);
这种情况怎么处理?很简单,使用类型断言就不会报错了:
const memoryUsage = Math.floor((pod.metrics?.memoryUsage as number) /1024 / 1024);