学习目标
- 1掌握函数的类型注解
- 2学会使用可选参数和默认参数
- 3理解剩余参数和解构参数
- 4掌握函数重载
- 5为博客编写实用的工具函数
上一章我们做了什么
在上一章中,我们深入学习了接口的高级特性:
- 可选属性(?`)让接口更灵活
- 只读属性(readonly)保护重要数据
- 接口继承避免重复定义
- 函数类型接口定义函数的"形状"
我们为博客设计了完整的类型系统。但有了类型定义,还需要函数来操作这些数据。
这一章,我们会学习函数的各种特性,为博客编写实用的工具函数。
问题:没有类型约束的函数容易出错
想象一下这个场景:你写了一个格式化日期的函数。
JavaScript版本:
function formatDate(date) {
return date.toLocaleDateString();
}
formatDate(new Date()); // "2024/1/1" - 正确
formatDate("2024-01-01"); // "2024/1/1" - 可能正确
formatDate(1234567890); // "1970/1/15" - 错误!
formatDate(null); // 报错!
JavaScript不会告诉你传入的参数类型不对,直到运行时才会出错。
TypeScript版本:
function formatDate(date: Date): string {
return date.toLocaleDateString();
}
formatDate(new Date()); // ✓ 正确
formatDate("2024-01-01"); // ✗ 编译报错!类型"string"不能赋值给"Date"
formatDate(1234567890); // ✗ 编译报错!
formatDate(null); // ✗ 编译报错!
TypeScript在你写代码时就能发现问题。
函数类型注解的三个部分
函数的类型注解包括三个部分:
function formatDate(date: Date): string {
// ^^^^^^^^^^^ ^^^^^^
// 参数类型 返回值类型
return date.toLocaleDateString();
}
| 部分 | 作用 | 示例 |
|------|------|------|
| 参数类型 | 确保传入正确类型的参数 | date: Date |
| 返回值类型 | 确保函数返回正确类型的值 | : string |
| 函数类型 | 定义函数的完整"形状" | (date: Date) => string |
一个原则: 函数参数必须手动指定类型,返回值类型通常可以让TypeScript推断。
函数声明 vs 箭头函数
问题:有些参数用户可能不想传
在博客系统中,我们经常需要处理可选参数。比如:
- 格式化日期时,用户可能想指定格式,也可能用默认格式
- 查询文章时,用户可能想分页,也可能查询所有
- 创建文章时,用户可能想指定状态,也可能用默认值
**解决方案:可选参数和默认参数
可选参数:让用户选择
默认参数:给参数一个默认值
类比:可选参数就像套餐选择
把函数参数想象成餐厅套餐:
主菜(必需):红烧肉
配菜(可选):青菜 / 豆腐 / 不选
饮料(可选):可乐 / 雪碧 / 不选
- 可选参数:你可以不选,但没有默认值
- 默认参数:你可以不选,系统自动帮你选一个
// 可选参数:不选就是undefined
function orderMainCourse(main: string, side?: string) {}
// 默认参数:不选就是"青菜"
function orderMainCourse(main: string, side: string = "青菜") {}
问题:参数数量不确定怎么办?
有时候,函数需要接收不定数量的参数。比如:
- 计算多个数字的和
- 合并多个数组
- 格式化多个标签
如果参数数量固定,我们得写很多重载。有没有更好的方法?
解决方案:剩余参数
剩余参数用 ... 语法,将多个参数收集到一个数组中。
剩余参数:收集不定数量的参数
问题:同一个函数处理不同类型的参数?
在博客系统中,我们经常需要一个函数处理不同类型的参数。比如:
getPostById(id):id可能是字符串,也可能是数字formatDate(date):date可能是Date对象,也可能是时间戳
如果写两个函数,名字一样但参数不同,TypeScript会报错。
解决方案:函数重载
函数重载允许我们定义多个函数签名,但只有一个实现。
函数重载:处理多种参数类型
实战:为博客编写工具函数
现在让我们把学到的知识应用到博客项目中,编写一些实用的工具函数。
我们要实现:
- 日期格式化函数
- 文章搜索函数
- 标签处理函数
- 分页函数
博客工具函数集合
常见错误
学习函数时,初学者常犯以下错误:
错误1:可选参数位置错误
// 错误:可选参数必须在必需参数后面
function greet(greeting?: string, name: string) {} // 编译报错!
// 正确
function greet(name: string, greeting?: string) {}
错误2:忘记处理可选参数的undefined
function greet(name: string, greeting?: string) {
// 错误:greeting可能是undefined
return greeting.toUpperCase(); // 可能报错!
// 正确:先检查
return greeting ? greeting.toUpperCase() : "HELLO";
}
错误3:剩余参数位置错误
// 错误:剩余参数必须在最后
function bad(...args: number[], last: string) {} // 编译报错!
// 正确
function good(last: string, ...args: number[]) {}
错误4:函数重载签名和实现不匹配
// 错误:实现签名不在重载列表中
function add(x: number, y: number): number; // 重载1
function add(x: string, y: string): string; // 重载2
function add(x: any, y: any): any { // 实现
return x + y;
}
// add(1, "2") // 错误:没有匹配的重载
动手实践
现在轮到你了!让我们为博客编写更多的工具函数。
练习1:编写文章摘要生成函数
function generateExcerpt(content: string, maxLength: number = 100): string
- 从文章内容中提取前maxLength个字符作为摘要
- 如果内容长度不超过maxLength,返回原内容
- 否则截断并添加"..."
练习2:编写文章排序函数
function sortPosts(
posts: Post[],
sortBy: "createdAt" | "viewCount" | "title",
order: "asc" | "desc" = "desc"
): Post[]
- 按指定字段排序
- 支持升序和降序
练习3:编写标签统计函数
function countTags(posts: Post[]): Map<string, number>
- 统计每个标签出现的次数
- 返回Map对象
练习参考答案
本章小结
这一章我们学习了函数的各种特性:
1. 函数类型注解
- 参数类型:确保传入正确类型的参数
- 返回值类型:确保函数返回正确类型的值
2. 可选参数和默认参数
- 可选参数用
?标记,参数可以省略 - 默认参数直接赋值,省略时使用默认值
3. 剩余参数
- 用
...语法,将多个参数收集到数组中 - 必须放在参数列表最后
4. 函数重载
- 定义多个函数签名,处理不同类型的参数
- 只有一个函数实现
5. 实用工具函数
- 日期格式化
- 文章搜索和分页
- 标签处理
下一章预告:
在下一章中,我们会学习类和面向对象编程:
- 类的定义和构造函数
- 访问修饰符(public、private、protected)
- 继承和方法重写
- 用类封装博客的核心逻辑
让我们继续吧!
交互式练习
函数练习
编写博客工具函数
章节测验
如何标记可选参数?