TS函数重载和联合类型的区别

281 阅读3分钟

重载的语法

在ts中,一个函数拥有多个签名的写法被称为函数的重载。熟悉Java的人很容易理解,重载就是一个function在传入不同的参数时拥有特定的不同的返回值。比如下面这个函数:

function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };
function pickCard(x): any {
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

请注意,function pickCard(x): any并不是重载列表的一部分,因此这里只有两个重载:一个是接收对象另一个接收数字。 以其它参数调用pickCard会产生错误。 这样做的好处主要有以下几点:

  1. 提高代码的可读性和可维护性。
  2. 增强代码的灵活性:函数重载允许我们使用相同的函数名,但传入不同数量或不同类型的参数,从而创建出多个方法或产生不同的结果。
  3. 提供类型安全:TypeScript 的类型系统会在编译时检查函数的使用是否符合其定义的重载列表,从而提供类型安全。

和联合类型的区别

我们可能会有这种疑问,上面的例子我们完全可以写成:

function pickCard(x: {suit: string; card: number; }[] | number): number | {suit: string; card: number; } {
    // Check to see if we're working with an object/array
    // if so, they gave us the deck and we'll pick the card
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

那么重载的本质是什么呢?

  • 重载:
function f1(a: string) { }
function f1(a: number) { }
function f1(a: any) : any {}
  • 联合类型:
function f1(a: string | number):string | number { }

其实答案就是: 对于函数参数类型,函数重载可以比联合类型和泛型类型更具体。

函数重载将特定的输入类型映射到特定的返回类型。使用联合,我们只知道返回是有效类型之一,但是失去了输入和输出之间的关联。这可能是问题,也可能不是问题,这取决于使用函数的方式和位置。但这就是不同之处。

函数重载允许我们为不同的参数类型定义不同的行为,而且每个重载都有一个明确的返回类型。这意味着,我们可以根据参数类型来确定返回类型,这是联合类型无法做到的。

对于上面的例子,调用f1,我们得到的return type永远是 stirng | number, 失去了特异性。

泛型

还有第三个选项是typescript generics。这允许我们在接受无限多类型的同时保持特异性。我们告诉typescript“查看参数a的类型,并将其称为T”。然后我们得到这个类型变量T,我们可以在返回类型中使用它。在这里,我们只是直接返回相同类型的T,但是你可以用它做更多的事情。

function generic<T>(a: T): T { 
    return a; 
}

const bool = generic<boolean>(false) // bool会被推导为boolean类型