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开发干货

相关推荐
宁雨桥1 天前
Service Worker:前端离线化与性能优化的核心技术
前端·性能优化
IT_陈寒1 天前
SpringBoot实战:这5个隐藏技巧让我开发效率提升200%,90%的人都不知道!
前端·人工智能·后端
ObjectX前端实验室1 天前
【图形编辑器架构】节点树与渲染树的双向绑定原理
前端·计算机图形学·图形学
excel1 天前
Vue2 与 Vue3 生命周期详解与对比
前端
一只猪皮怪51 天前
React 18 前端最佳实践技术栈清单(2025版)
前端·react.js·前端框架
Misnice1 天前
React渲染超大的字符串
前端·javascript·react.js
天天向上的鹿茸1 天前
用矩阵实现元素绕不定点旋转
前端·线性代数·矩阵
李鸿耀1 天前
主题换肤指南:设计到开发的完整实践
前端
带娃的IT创业者1 天前
TypeScript + React + Ant Design 前端架构入门:搭建一个 Flask 个人博客前端
前端·react.js·typescript
二十雨辰1 天前
vite如何处理项目中的资源
开发语言·javascript