写在最前:笔者从去年就开始接触学习ts,但是看了几次文档都朦朦的,所以本系列旨在分享学习ts路上一些较为深入的理解(结合一个实际例子去讲解)
函数重载
函数重载,我们可以这样理解,相同的函数名,但是参数(入参或返参)的类型或者个数不同。
function test(a: number): number;
function test(x): void {
...
}
可以看到上面的代码就是一个函数重载。
函数重载的概念比较简单,但是有一点我们需要注意下: 在定义重载的时候,一定要把最精确的定义放在最前面。
那函数重载有什么用呢,或者说有什么场景下用函数重载呢?
其实很简单,就是当你的函数参数类型有判断条件时,大白话就是,你想写个if else 给参数的类型时,这时候就需要用到函数重载了。
接下来,举个实际的例子。
约束函数参数类型
场景描述: 有这样一个函数,根据对象数组中对象的时间进行排序,然后时间的key可以自定义
先上代码:
function sortDatas(data: Record<string, unknown>[], timeKey = 'time') {
return data.sort((a, b) => a[timeKey] - b[timeKey]);
};
// The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.ts(2362)
可以看到,这里报错了,因为 a[timeKey] 类型是unknown, 那你这里都用上减法了,肯定是知道类型的。这个我们先不管。
我们再梳理一下,函数的类型声明这里我们需要做到什么:
- data是对象数组
- timeKey是data里的对象的key
- 当没有自定义timeKey时,data里的对象需要有time这个key,且对应的值的类型为number
- 当自定义timeKey时,data里的对象需要有time这个key,且对应的值的类型为number
接下来我们拆解地来看如何实现:
// 1. 简单定义 就能做到
function sortDatas(data: Record<string, unknown>[], timeKey = 'time') {
return data.sort((a, b) => a[timeKey] - b[timeKey]);
};
// 2. 没简单了,我们上泛型和keyof
function sortDatas<T extends Record<string, unknown>>(data: T[], timeKey?: keyof T) {
const key = timeKey ?? 'time';
return data.sort((a, b) => a[key] - b[key]);
}
// 3. 写不了...
可以看到,第三点我们就写不了了,因为我们没法在函数声明里写If else, 这时候函数重载就上场了,我们先把各种情况单独写好
// 3. 假设就是默认用time,即不传第二个参数
function sortDatas<T extends Record<'time', number>>(data: T[]) {
const key = 'time';
return data.sort((a, b) => a[key] - b[key]);
}
// 4. 假设就是传了第二个参数,这时候需要用第二个泛型K反向约束T
function sortDatas<T extends Record<K, number>, K extends string>(data: T[], timeKey: K) {
const key = timeKey;
return data.sort((a, b) => a[key] - b[key]);
}
到了这个时候,我们的代码已经组织的差不多了。 这时候我们来看最终的代码并加上一些测试例子:
export function sortData<T extends Record<'time', number>>(data: T[]): T[];
export function sortData<T extends Record<K, number>, K extends string>(data: T[], timeKey: K): T[];
export function sortData<T extends Record<K, number>, K extends string>(
data: T[],
timeKey?: K
): T[] {
const key = timeKey ?? ('time' as keyof T); // 需要手动约束下time这个关键字
return data.sort((a, b) => a[key] - b[key]);
}
sortData([{ a: 123 }]); // TS 报错, 因为没有a这个关键词
sortData([{ time: '123' }]); // TS 报错,因为 '123' 不是 number 类型
sortData([{ time: 123 }]);
sortData([{ a: 123 }], 'a');
优化我们的函数重载声明
可以参考一下Dayjs
interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs
(date: ConfigType, format: string, timezone?: string): Dayjs
}
const tz: DayjsTimezone