学习目标
- 1掌握TypeScript的基础类型
- 2学会为变量和函数添加类型注解
- 3理解类型推断的工作原理
- 4为博客项目定义完整的类型系统
上一章我们做了什么
在上一章中,我们创建了博客项目,并用一个简单的Post接口定义了文章的结构。
但你可能注意到了一个问题:我们的类型太简单了。一个真正的博客系统需要:
- 不同状态的文章(草稿、已发布、已归档)
- 多种标签
- 浏览次数统计
- 作者信息
这一章,我们会学习TypeScript的基础类型,然后用它们来完善博客的类型系统。
问题:为什么需要类型?
想象一下这个场景:你在写一个函数来显示文章的发布日期。
JavaScript版本:
function showDate(date) {
return date.toLocaleDateString();
}
showDate("2024-01-01"); // 可能正常工作
showDate(1234567890); // 返回一个奇怪的日期
showDate(null); // 报错!
JavaScript不会告诉你传入的参数类型不对,直到运行时才会出错。
TypeScript版本:
function showDate(date: Date): string {
return date.toLocaleDateString();
}
showDate("2024-01-01"); // 编译时报错!类型"string"不能赋值给"Date"
TypeScript在你写代码时就能发现问题,而不是等到用户使用时才出错。这就是类型安全的价值。
基础类型一览
TypeScript提供了几种基础类型,就像积木一样,我们可以用它们来构建复杂的类型。
原始类型(最基础的积木):
| 类型 | 说明 | 示例 |
|------|------|------|
| string | 字符串 | "hello" |
| number | 数字 | 42, 3.14 |
| boolean | 布尔值 | true, false |
| null | 空值 | null |
| undefined | 未定义 | undefined |
特殊类型:
| 类型 | 说明 | 何时使用 |
|------|------|----------|
| any | 任意类型 | 尽量避免使用 |
| void | 无返回值 | 函数不返回东西时 |
| never | 永不返回 | 函数总是抛出错误时 |
| unknown | 未知类型 | 比any更安全的替代品 |
复合类型(用积木搭建的复杂结构):
| 类型 | 说明 | 示例 |
|------|------|------|
| 数组 | 同类型集合 | number[], Array<string> |
| 元组 | 固定长度和类型 | [string, number] |
| 枚举 | 命名常量 | enum Status { Active } |
在博客中使用基础类型
类型注解 vs 类型推断
TypeScript有两种方式确定变量的类型:
1. 类型注解(手动指定)
const title: string = "我的博客";
你明确告诉TypeScript:这个变量是string类型。
2. 类型推断(自动推断)
const title = "我的博客"; // TypeScript自动推断为string
TypeScript根据赋的值自动推断类型。
什么时候用哪种?
| 场景 | 建议 | |------|------| | 变量初始化时就有值 | 可以省略,让TS推断 | | 函数参数 | 必须手动指定 | | 函数返回值 | 建议手动指定 | | 类型不明显时 | 手动指定 |
一个原则: 当类型明显时,可以省略注解;当类型不明显或需要明确时,添加注解。
类型推断的实际应用
枚举:定义文章状态
在博客系统中,文章有不同的状态:草稿、已发布、已归档。我们可以用枚举来定义这些状态。
枚举是什么?
枚举就像一个菜单,列出了所有可能的选项。比如餐厅的菜单上有"红烧肉"、"宫保鸡丁"、"鱼香肉丝"——你只能从这些选项中选择。
为什么用枚举?
- 避免拼写错误:
"published"容易拼错,Status.Published不会 - 代码提示:IDE会自动显示所有选项
- 集中管理:修改状态值只需要改一处
用枚举定义文章状态
完善博客的类型系统
现在我们有了足够的知识来完善博客的类型系统。
我们需要定义:
- 文章状态(枚举)
- 文章接口(包含所有属性)
- 创建/更新文章的输入类型
设计原则:
- 只读属性用
readonly标记 - 可选属性用
?标记 - 自动生成的字段(如id、createdAt)不放在输入类型中
完整的博客类型定义
常见错误
学习类型系统时,初学者常犯以下错误:
错误1:过度使用any
// 不推荐
let data: any = "hello";
data = 42; // 不会报错,但可能不是你想要的
// 推荐
let data: string | number = "hello";
data = 42; // 明确允许string或number
错误2:忘记数组类型
// 不推荐
const tags = []; // 类型是 any[]
// 推荐
const tags: string[] = [];
错误3:混淆null和undefined
// null:明确表示"没有值"
const title: string | null = null;
// undefined:表示"未定义"
const title: string | undefined = undefined;
动手实践
现在轮到你了!让我们为博客添加更多的类型定义。
练习1:定义用户类型 为博客系统定义一个User接口,包含:
- id: string
- name: string
- email: string
- avatar?: string(可选)
- bio?: string(可选)
练习2:定义评论类型 为博客系统定义一个Comment接口,包含:
- id: string
- postId: string(关联的文章ID)
- content: string
- authorName: string
- createdAt: Date
练习3:使用枚举 定义一个CommentStatus枚举,包含:
- Pending(待审核)
- Approved(已批准)
- Rejected(已拒绝)
练习参考答案
本章小结
这一章我们学习了:
1. 基础类型
- string、number、boolean:最常用的原始类型
- null、undefined:表示空值
- any、unknown:特殊类型,尽量避免any
2. 类型注解 vs 类型推断
- 类型注解:手动指定类型
- 类型推断:TypeScript自动推断
- 函数参数必须手动指定类型
3. 枚举
- 用于定义一组命名常量
- 避免拼写错误,提供代码提示
4. 完善的类型系统
- Post接口:文章的完整结构
- CreatePostInput:创建文章时的输入
- UpdatePostInput:更新文章时的输入
下一章预告:
在下一章中,我们会学习接口的更多特性:
- 接口继承
- 可选属性和只读属性
- 函数类型接口
这些特性会让我们博客的类型系统更加强大!
交互式练习
类型注解练习
为变量添加正确的类型注解
章节测验
以下哪个是TypeScript的基础类型?