TypeScript: TypeScript 函数类型
最后更新:2026-08-26
函数是 JavaScript 的核心构造,TypeScript 为函数的参数和返回值添加了完整的类型系统——让函数的"输入"和"输出"都有据可查。
1. 函数类型注解
(1) 参数和返回值类型
TYPESCRIPT
function add(a: number, b: number): number {
return a + b;
}
let result = add(1, 2); // result 推断为 number
// add("1", "2"); // ❌ 参数类型不匹配
// add(1); // ❌ 参数数量不够
(2) 返回值类型推断
TypeScript 能根据 return 语句推断返回值类型,但建议显式标注:
TYPESCRIPT
// 返回值推断——能推断但不推荐(大函数容易推断错误)
function multiply(a: number, b: number) {
return a * b; // 推断返回 number
}
// 显式标注——推荐(文档作用 + 编译时捕获错误)
function divide(a: number, b: number): number {
if (b === 0) throw new Error("除数不能为零");
return a / b;
}
(3) void 返回值
函数没有返回值时用 void:
TYPESCRIPT
function log(message: string): void {
console.log(message);
// 没有 return 语句,或者 return; —— 都是 void
}
(4) never 返回值
函数永远不会有返回值时用 never——要么抛异常,要么无限循环:
TYPESCRIPT
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) { }
}
| 返回类型 | 含义 |
|---|---|
void |
函数正常结束,但没有返回值 |
never |
函数永远不会结束(抛异常或死循环) |
▶ 示例:带类型的工具函数
TYPESCRIPT
function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}
function formatCurrency(amount: number, symbol: string = "¥"): string {
return symbol + amount.toFixed(2);
}
console.log(clamp(150, 0, 100)); // 100
console.log(clamp(-5, 0, 100)); // 0
console.log(clamp(50, 0, 100)); // 50
console.log(formatCurrency(99.5)); // ¥99.50
console.log(formatCurrency(42, "$")); // $42.00
输出:
TEXT
📖 仅展示
100
0
50
¥99.50
$42.00
2. 可选参数与默认参数
(1) 可选参数 ?
TYPESCRIPT
function greet(name: string, title?: string): string {
if (title) {
return `${title} ${name}`;
}
return `你好,${name}`;
}
console.log(greet("Charlie")); // "你好,Charlie"
console.log(greet("Charlie", "工程师")); // "工程师 Charlie"
⚠️ 规则:可选参数必须在必选参数后面。
function greet(title?: string, name: string) 会报错。
(2) 默认参数
TYPESCRIPT
function createUser(name: string, role: string = "viewer", active: boolean = true): string {
return `${name},角色:${role},${active ? "活跃" : "未激活"}`;
}
console.log(createUser("Charlie")); // Charlie,角色:viewer,活跃
console.log(createUser("Diana", "editor")); // Diana,角色:editor,活跃
console.log(createUser("Eric", "admin", false)); // Eric,角色:admin,未激活
(3) 可选参数 vs 默认参数
| 特性 | 可选参数 title?: string |
默认参数 role: string = "viewer" |
|---|---|---|
| 调用时 | 可省略,值为 undefined | 可省略,值为默认值 |
| 类型 | string | undefined |
string(不是 undefined 联合) |
| 推荐 | 值确实可能为 undefined | 大多数情况——有合理默认值更好 |
3. 剩余参数
剩余参数用 ... 语法收集多余参数为数组:
TYPESCRIPT
function sum(first: number, ...rest: number[]): number {
return rest.reduce((total, n) => total + n, first);
}
console.log(sum(1)); // 1
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4)); // 10
(1) 剩余参数必须是数组类型
TYPESCRIPT
function logAll(prefix: string, ...messages: string[]): void {
messages.forEach(msg => console.log(`${prefix}: ${msg}`));
}
logAll("DEBUG", "启动服务", "连接数据库", "就绪");
输出:
TEXT
📖 仅展示
DEBUG: 启动服务
DEBUG: 连接数据库
DEBUG: 就绪
4. 函数类型表达式
函数也可以作为值传递——这时需要用函数类型表达式描述类型:
(1) 类型别名定义函数类型
TYPESCRIPT
type MathOperation = (a: number, b: number) => number;
let add: MathOperation = (a, b) => a + b;
let subtract: MathOperation = (a, b) => a - b;
let multiply: MathOperation = (a, b) => a * b;
console.log(add(10, 5)); // 15
console.log(subtract(10, 5)); // 5
console.log(multiply(10, 5)); // 50
(2) 回调函数类型
函数类型最常见的用途——描述回调函数的签名:
TYPESCRIPT
function fetchData(url: string, onSuccess: (data: string) => void, onError: (error: Error) => void): void {
// 模拟异步操作
if (url.startsWith("https://")) {
onSuccess("数据加载成功");
} else {
onError(new Error("仅支持 HTTPS"));
}
}
fetchData(
"https://api.example.com",
data => console.log(data), // ✅ (data: string) => void
error => console.log(error.message) // ✅ (error: Error) => void
);
(3) 用接口定义函数类型
TYPESCRIPT
interface Comparator {
(a: number, b: number): number;
}
let ascending: Comparator = (a, b) => a - b;
let descending: Comparator = (a, b) => b - a;
let nums = [3, 1, 4, 1, 5];
console.log([...nums].sort(ascending)); // [1, 1, 3, 4, 5]
console.log([...nums].sort(descending)); // [5, 4, 3, 1, 1]
5. 函数重载
函数重载让同一个函数名支持不同的参数类型组合——TypeScript 的重载是"声明 + 实现"的模式:
(1) 基本语法
TYPESCRIPT
// 重载签名——描述每种参数组合的返回类型
function format(value: number): string;
function format(value: string): string;
function format(value: Date): string;
// 实现签名——必须兼容所有重载签名
function format(value: number | string | Date): string {
if (typeof value === "number") {
return value.toFixed(2);
} else if (typeof value === "string") {
return value.trim();
} else {
return value.toISOString();
}
}
console.log(format(3.14)); // "3.14"
console.log(format(" hello ")); // "hello"
console.log(format(new Date())); // "2024-..."
(2) 重载的顺序
TypeScript 按重载声明的顺序从上到下匹配——更具体的重载应放在前面:
TYPESCRIPT
// ✅ 正确顺序——具体的在前
function process(value: string): string;
function process(value: any): unknown;
// ❌ 错误顺序——any 会匹配所有参数,后面的重载永远不会被调用
// function process(value: any): unknown;
// function process(value: string): string;
▶ 示例:重载实现类型安全的事件处理
TYPESCRIPT
function on(event: "click", handler: (x: number, y: number) => void): void;
function on(event: "keydown", handler: (key: string) => void): void;
function on(event: string, handler: Function): void {
console.log(`注册事件:${event}`);
// 实际实现省略...
}
// 调用时自动获得正确的参数类型
on("click", (x, y) => {
console.log(`点击位置:(${x}, ${y})`); // x, y 推断为 number
});
on("keydown", (key) => {
console.log(`按下按键:${key}`); // key 推断为 string
});
6. 箭头函数的类型
(1) 箭头函数的类型注解
TYPESCRIPT
const add = (a: number, b: number): number => a + b;
// 作为回调时,参数类型通常可以推断
const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2); // n 推断为 number
const asStrings = nums.map(n => String(n)); // n 推断为 number
(2) 箭头函数与 this
箭头函数不绑定自己的 this——它继承外层的 this:
TYPESCRIPT
class Timer {
seconds = 0;
start() {
// 箭头函数——this 指向 Timer 实例
setInterval(() => {
this.seconds++;
console.log(`${this.seconds}秒`);
}, 1000);
}
startBroken() {
// 普通函数——this 指向全局或 undefined(严格模式)
setInterval(function () {
// this.seconds++; // ❌ 运行时 this 不是 Timer
}, 1000);
}
}
❓ 常见问题
Q 函数返回值类型要不要显式标注?
A 建议标注。虽然 TypeScript 能推断,但显式标注有三个好处:(1) 文档作用——一眼看出返回类型 (2) 防止意外返回错误类型 (3) 更好的错误定位——报错在函数签名而非调用处。小函数(单行 return)可以省略,超过3行的函数建议标注。
Q void 和 undefined 返回值有什么区别?
A void 表示"函数不关心返回值"——函数可以有 return 语句也可以没有,调用者不应该使用返回值。undefined 是具体的类型——函数明确返回 undefined。实际开发中,没有返回值的函数都用 void。
Q 函数重载和联合类型参数有什么区别?
A 联合类型
function fn(x: string | number) 只能描述"参数可以是这些类型之一",但所有参数组合返回同一类型。重载可以描述"不同参数组合返回不同类型"——这是联合类型做不到的。如果只是参数类型灵活但返回类型统一,用联合类型更简洁;需要不同返回类型时用重载。Q 默认参数和可选参数能同时用吗?
A 不能。
function fn(x?: string = "hi") 是语法错误——默认参数已经隐含了"可省略"的含义,不需要再加 ?。x: string = "hi" 本身就等于 x?: string 但值是 "hi" 而不是 undefined。📖 小节
- 函数类型注解:参数类型必须标注,返回值建议标注;void 表示无返回值,never 表示永不返回
- 可选参数用
?(值可能为 undefined),默认参数用= 默认值(值有默认值更安全) - 剩余参数
...args: 类型[]收集多余参数为数组 - 函数类型表达式
type Fn = (参数) => 返回值用于描述回调、高阶函数 - 函数重载是"声明+实现"模式——声明描述每种参数组合的返回类型,实现必须兼容所有重载
📝 作业
- 基础题(难度⭐):写三个函数——
add(a, b)加法、subtract(a, b)减法、multiply(a, b)乘法,参数和返回值都是 number。用函数类型表达式统一声明类型。 - 进阶题(难度⭐⭐):写一个函数
buildQuery(params: Record<string, string | number | boolean>),把对象转成 URL 查询字符串。例如{ page: 1, size: 10, active: true }→"page=1&size=10&active=true"。 - 挑战题(难度⭐⭐⭐):用函数重载实现
createElement(tag, props):当 tag 为 "input" 时 props 必须有type: string,当 tag 为 "a" 时 props 必须有href: string,当 tag 为 "div" 时 props 是通用对象。每种情况返回不同的类型描述对象。