函数类型与重载

写给初学者:每个概念都从”为什么需要它”开始讲,配合通俗比喻和完整可运行代码。


为什么需要函数类型?

在 TypeScript 里,函数是”一等公民”——和数字、字符串一样,可以当变量存、当参数传、当返回值返回。这时候就需要描述”这个函数长什么样”。


函数声明与类型标注

// 方式 1:函数声明
function add(a: number, b: number): number {
    return a + b;
}
 
// 方式 2:箭头函数
const multiply = (a: number, b: number): number => a * b;
 
// 方式 3:函数类型标注
type MathFunc = (a: number, b: number) => number;
const divide: MathFunc = (a, b) => a / b;

可选参数和默认参数

// 可选参数(? 标记,必须放在必填参数后面)
function greet(name: string, greeting?: string): string {
    return `${greeting || "你好"}, ${name}!`;
}
 
// 默认参数(比可选参数更简洁)
function greet2(name: string, greeting: string = "你好"): string {
    return `${greeting}, ${name}!`;
}
 
greet2("张三");              // "你好, 张三!"
greet2("张三", "早上好");     // "早上好, 张三!"

剩余参数(rest parameters)

function sum(...numbers: number[]): number {
    return numbers.reduce((total, n) => total + n, 0);
}
 
sum(1, 2, 3);           // 6
sum(10, 20, 30, 40);    // 100
sum();                   // 0
 
// 搭配其他参数
function log(level: string, ...messages: string[]) {
    console.log(`[${level}]`, ...messages);
}
log("INFO", "用户登录", "IP: 127.0.0.1");

函数重载(Overloads)

有些函数根据参数类型不同,返回值类型也不同:

// 没有重载的问题
function double(value: string | number): string | number {
    if (typeof value === "string") return value + value;
    return value * 2;
}
const r1 = double("ha");   // r1 的类型是 string | number(不确定!)
 
// 用函数重载解决
function double2(value: string): string;      // 传 string 返回 string
function double2(value: number): number;      // 传 number 返回 number
function double2(value: string | number): string | number {
    if (typeof value === "string") return value + value;
    return value * 2;
}
 
const r2 = double2("ha");   // r2 的类型是 string(精确!)
r2.toUpperCase();            // ✅
const r3 = double2(21);     // r3 的类型是 number
r3.toFixed(2);               // ✅

通俗比喻:函数重载就像自动贩卖机——你投硬币(参数),机器根据你投的是纸币还是硬币(参数类型),给你不同面额的找零(返回值类型)。机器(实现)只有一台,但投币口(重载签名)有好几种。


回调函数类型

type Callback = (data: string, error?: string) => void;
 
function fetchData(url: string, callback: Callback) {
    if (url.includes("error")) {
        callback("", "请求失败");
    } else {
        callback('{"name": "张三"}');
    }
}
 
fetchData("https://api.example.com/user", (data, error) => {
    if (error) {
        console.log("出错了:", error);
    } else {
        console.log("拿到数据:", data);
    }
});
 
// 事件处理函数类型
type EventHandler = (event: { type: string; target: unknown }) => void;
const handleClick: EventHandler = (event) => {
    console.log("点击了:", event.type);
};

速查表

概念一句话解释关键代码
可选参数函数参数可以不传function f(name: string, age?: number)
默认参数参数有默认值function f(name: string, g = "你好")
剩余参数收集多余参数为数组function f(...args: number[])
函数重载同一函数根据参数类型返回不同类型多个签名 + 一个实现
void函数没有返回值function f(): void

速记卡(面试闪卡)

Q1:一句话讲清「函数类型与重载」到底是什么? A:TS 里函数是头等公民需描述其”长相”:用类型标注声明签名,重载用多个签名+一个实现,让不同参数类型返回精确类型而非联合类型。

Q2:函数类型与声明方式 —— 怎么理解? A:TS 里函数和数、字符串一样能存能传能返回,于是要描述”这个函数长什么样”。三种写法:函数声明 function、箭头 const f = ()=>、类型标注 type F = (a,b)=>c。类型标注让变量只能装符合同签名的函数。

Q3:可选 / 默认 / 剩余参数 —— 怎么理解? A:可选参数用 ? 且必须放必填后;默认参数更简洁,不传就用默认值;剩余参数 …nums 把多余参数收成数组,还能搭配其他参数。三者解决”参数个数不固定”的问题,是灵活函数签名的基础。

Q4:函数重载 Overloads —— 怎么理解? A:没重载时,double(‘ha’) 返回类型被推成 string|number,后续 .toUpperCase 报错。重载写多个签名(传 string 返 string、传 number 返 number)+ 一个实现,调用处就能拿到精确类型。像自动贩卖机:投币口几种,机器只有一台。

Q5:回调函数类型 —— 怎么理解? A:事件处理、异步回调都要给函数”画像”:type Callback = (data, error?)=>void。fetchData 收 url 和回调,成功带 data、失败带 error,调用方在回调里分流。事件处理函数同理用 (event)=>void 约束,保证传进来的函数形状正确。

Q6:核心速记主线有哪些?

  • 函数类型:头等公民需类型标注描述签名
  • 可选 ? / 默认 / 剩余 … 三类参数
  • 重载:多签名 + 一实现,返回精确类型
  • 比喻:自动贩卖机,多投币口一台机器
  • 回调类型:用 type 约束 (data, error?) => void

口诀 A:TS 函数是公民,类型标注画其形; 可选默认剩余参,个数不固定也行; 重载多签一实现,返回精确不联名; 回调画像 type 定,投币贩卖机口清。

相关链接