TypeScript 函数重载入门:让你的函数签名更精确

一、什么是函数重载?

函数重载的核心思想是:对外声明多种调用方式,对内用一个统一的实现来处理。

一个完整的函数重载包含两个主要部分:

  1. 重载签名 :定义了函数的各种调用形式,包括参数的类型、数量和返回值的类型。这些签名没有函数体。
  2. 实现签名 :这是函数 唯一 的实现,它包含函数体。它的签名必须能够 兼容 所有重载签名。

示例: 一个 add 函数,既可以用于数字相加,也可以用于字符串拼接。

typescript 复制代码
// 1. 重载签名 (Overload Signatures)
function add(x: number, y: number): number;
function add(x: string, y: string): string;

// 2. 实现签名 (Implementation Signature)
function add(x: any, y: any): any {
    // 3. 函数体实现
    if (typeof x === 'number' && typeof y === 'number') {
        return x + y;
    }
    if (typeof x === 'string' && typeof y === 'string') {
        return x + y;
    }
    throw new Error('Invalid arguments');
}

// 调用
const numSum = add(5, 10); // numSum 的类型被推断为 number
console.log(numSum); // 15
const strSum = add('Hello, ', 'World!'); // strSum 的类型被推断为 string
console.log(strSum); // Hello, World!

分析:

  • 外部可见性 :当外部代码调用 add 函数时,TypeScript 会看到两个重载签名。它会根据你传入的参数,从上到下查找第一个匹配的签名。
  • 内部实现 :实现签名 function add(x: any, y: any): any 对外部是不可见的,你不能用 (any, any) 的方式直接调用 add 函数(除非强制类型转换)。
  • 兼容性 :实现签名必须涵盖所有重载签名。在这里,x: any, y: any 可以接受 (number, number)(string, string) 的情况。

二、重载的顺序

函数重载的顺序至关重要,因为 TypeScript 在解析调用时会 按顺序检查。一旦找到匹配的签名,它就会停止搜索。

  • 顺序一般是代码中从上而下的顺序。
  • 注:在有类型包含关系的情况下一般有小而大,例如:先number,再any
typescript 复制代码
function move(p: Point): void; 
function move(p: any): void; 

// ... 实现 ...
function move(p: any): void {
  // ...
}

move({ x: 1, y: 2 }); // OK, p 的类型被正确推断为 Point

三、常见的几种函数重载的优化方案

1. 联合类型的应用

参数类型不同,但逻辑和返回类型相似

typescript 复制代码
// 使用重载 (显得冗余)
function printId(id: number): void;
function printId(id: string): void;
function printId(id: number | string): void {
  console.log("Your ID is: " + id);
}

// 使用联合类型 (更简洁)
function printIdSimple(id: number | string): void {
  console.log("Your ID is: " + id);
}

2. 可选参数 或 默认参数

如果只是参数数量不同,可以使用 可选参数默认参数

typescript 复制代码
// 使用重载
function greet(person: string): string;
function greet(person: string, greeting: string): string;
function greet(person: string, greeting?: string): string {
  return `${greeting || "Hello"}, ${person}!`;
}

// 使用可选参数 (更简洁)
function greetSimple(person: string, greeting?: string): string {
    return `${greeting || "Hello"}, ${person}!`;
}

3. 泛型

当函数的输入类型和输出类型之间存在一种模式或关联,但具体的类型是可变的,泛型 是最佳选择。

typescript 复制代码
// 使用重载 (无法穷举所有类型)
function getFirstElement(arr: number[]): number | undefined;
function getFirstElement(arr: string[]): string | undefined;
function getFirstElement(arr: any[]): any | undefined {
    return arr[0];
}

// 使用泛型 (终极解决方案)
function getFirstElementGeneric<T>(arr: T[]): T | undefined {
    return arr[0];
}

const firstNum = getFirstElementGeneric<number>([1, 2, 3]); // 推断为 number
console.log(firstNum);
const firstStr = getFirstElementGeneric<string>(['a', 'b', 'c']); // 推断为 string
console.log(firstStr);

总结

如果你喜欢本教程,记得点赞+收藏!关注我获取更多JavaScript/TypeScript开发干货

相关推荐
阳光阴郁大boy44 分钟前
一个基于纯前端技术实现的五子棋游戏,无需后端服务,直接在浏览器中运行。
前端·游戏
石小石Orz1 小时前
效率提升一倍!谈谈我的高效开发工具链
前端·后端·trae
EndingCoder1 小时前
测试 Next.js 应用:工具与策略
开发语言·前端·javascript·log4j·测试·全栈·next.js
xw51 小时前
免费的个人网站托管-PinMe篇
服务器·前端
!win !1 小时前
免费的个人网站托管-PinMe篇
前端·前端工具
牧天白衣.1 小时前
CSS中linear-gradient 的用法
前端·css
军军3601 小时前
Git大型仓库的局部开发:分步克隆 + 指定目录拉取
前端·git
前端李二牛1 小时前
Vue3 特性标志
前端·javascript
coding随想1 小时前
JavaScript事件处理程序全揭秘:从HTML到IE的各种事件绑定方法!
前端
搞个锤子哟1 小时前
关键词匹配,过滤树
前端