blog

泛形与重载

泛形

上示例

一个简单的节流函数

function debounce(f: Function, delay: number = 3000) {
    let d = 0;
    return function (...arg: any) {
        if (d + delay > Date.now()) return;
        d = Date.now();
        f(...arg);
    };
}

// [ts] const log: (...arg: any) => void
const log = debounce((...arg: string[]) => {
    console.log(...arg);
});

ts 提示的居然不是 的参数集居然不是 string[],这并不是我们想要的,而且,而且它还是 any,直接等于没提示

不要急,不要慌,这里还有解决的方法


以下,我们借用泛形在重写一遍 debounce

function debounce<T extends unknown[]>(f: (...arg: T) => unknown, delay: number = 3000) {
    let date = 0;
    return function (...arg: T) {
        if (date + delay > Date.now()) return;
        date = Date.now();
        f(...arg);
    };
}

// [ts] const log: (str1: string, str2?: string, str3?: string) => void
const log = debounce((str1: string, str2?: string, str3?: string) => {
    console.log(str1, str2, str3);
});

我们使用泛形后,参数和类型都推断了出来,在使用 log 时,就可以获得良好的参数提示,而不是 any

泛形使用

以上的 debounce 其实还有另外一种使用方式

const log = debounce<[str1: string, str2?: string, str3?: string]>((str1, str2, str3) => {
    console.log(str1, str2, str3);
});

它其实和上面的方式是一毛一样的,不同的是,它在使用 debounce 时,直接传了[str1: string, str2?: string, str3?: string]


什么,传(chuan)了?

没错,就是传了。

泛形有一些函数参数的特点

  1. 可在使用时在确定它的类型
interface Person<AGE> {
    age: AGE;
    // ...
}

const person: Person<string> = {
    age: '19',
};

const person1: Person<number> = {
    age: 19,
};
  1. 可以有默认值
interface Person<AGE = number> {
    age: AGE;
}
// 不使用默认值
const person: Person<string> = {
    age: '19',
};
// 使用默认值
const person1: Person = {
    age: 19,
};
  1. 可以根据使用来推断类型
interface Person<AGE> {
    age: AGE;
}

// [ts]  const person: { age: number; }
const person = {
    age: 18,
};

泛形使用方式

// interface
interface Person<N, A> {
    name: N;
    age: A;
}

// type
type Person1<N, A> = {
    name: N;
    age: A;
};

// 函数
function Person2<N, A>(name: N, age: A) {}

// 类
class Person3<N, A> {
    name: N;
    age: A;
    constructor() {}

    say(name: N, age: A) {}
}

误区

错误,泛型不可以和 es6 一样使用 ... 来聚合多个参数(或许以后会吧)

interface Person<...ARGS> {}

重载

函数重载是一个强大的功能,在现有的流行库中有很多都使用上了

直接上示例吧


有时候我们需要一个这样的函数,在传参时设置该属性,在没传时则返回改属性。

如下

// 有参数时,则设置innerHTML参数,否则获取改属性
function html(content?: string): string | undefined {
    if (content) {
        el.innerHTML = content;
    } else {
        return el.innerHTML;
    }
}

但是,有了重载只有,我们可以这样写

/**
 * 获取innerHTML属性
 */
function html(): string;
/**
 * 设置innerHTML属性
 */
function html(content: string): void;
function html(content?: string): string | void {
    if (content) {
        el.innerHTML = content;
    } else {
        return el.innerHTML;
    }
}

// [ts] function html(): string (+1 overload)
// 获取innerHTML属性
html();

// [ts] function html(content: string): void (+1 overload)
// 设置innerHTML属性
html('123123');

如果 IDE 支持显示函数上面的注释,甚至还可以在分不同的重载显示不同注释提示

重载是什么

重载可以说时对函数输入输出的各种可能做不同的枚举,赋予使用时明确的提示


可以看上方示例,最下面的 function html(content?: string): string | void 我们可以叫做实现,而它上面两个可以叫做重载函数,上面有两个,也就是说,html有两个重载函数

实现:指真实的函数,真实的函数需要把所有重载的函数参数在这里写一遍

重载函数:要把函数参数的每一种传参数量和类型以及返回类型列举出来

重载函数每个都可以有不同的泛形,前提是你能方便的处理好实现时的输出

什么时候使用重载

参数长度不同函数输出类型不同

参数类型不同参数类型不同函数输出类型不同