TypeScript 学习路线图
这是一个从入门到进阶的 TypeScript 语法学习顺序建议。按照这个顺序学习,你的知识体系会更加系统和扎实。
第一阶段:基础核心 (掌握最基本的语法)
1. 基础类型 (Basic Types)
- 解释: 这是构建所有更复杂类型的基础。
- 核心类型 :
string
,number
,boolean
,null
,undefined
,symbol
,bigint
。 - 特殊类型 :
any
,unknown
,void
,never
。 - 适用场景 :
string
,number
,boolean
: 用于明确的原始值类型。这是 TypeScript 最基本的价值,可以防止类型混淆。- 示例 :
const username: string = "Alice";
- 示例 :
any
(任意类型) : 谨慎使用 。它告诉 TypeScript "放弃类型检查"。- 场景: 从旧的 JavaScript 项目逐步迁移时,作为临时方案;或者处理你完全无法预知其结构的第三方库数据。
- 警告 :
any
应该作为最后的选择,过度使用它会使 TypeScript 失去意义。
unknown
(未知类型) :any
的安全替代品 。它表示"类型未知,在检查之前不准操作"。- 场景 : 处理所有外部数据时的最佳实践,如 API 响应、用户输入、文件内容等。它强制你进行类型检查,使代码更安全。
- 示例 :
const response: unknown = JSON.parse(apiData);
void
: 用于表示一个函数没有任何返回值 。- 示例 :
function logMessage(message: string): void { console.log(message); }
- 示例 :
2. 数组 (Arrays) 与 元组 (Tuples)
- 数组 (
type[]
orArray<type>
) :- 解释 : 表示一个包含同一种类型元素的列表,长度不限。
- 场景: 当你需要一个列表,且其中所有元素的类型都相同时。
- 示例 : 一个商品 ID 列表
const productIds: number[] = [101, 102, 105];
或文章标签列表const tags: string[] = ["typescript", "guide"];
。
- 元组 (
[type1, type2, ...]
) :- 解释 : 一个固定长度 且每个位置的类型都已确定的数组。
- 场景: 当一个数组的不同索引位置代表不同且固定的含义时。
- 示例 : 表示 HTTP 状态
let httpStatus: [number, string] = [200, "OK"];
或者 React 的useState
Hook 的返回值const [count, setCount] = useState(0);
。
3. 函数 (Functions)
- 解释 : 为函数的参数 和返回值提供类型定义。这是 TypeScript 最核心、最强大的功能之一。
- 场景 : 为你写的每一个函数都添加类型!
- 确保输入正确 : 防止调用者传入错误类型的数据。 (
function greet(name: string)
) - 确保输出可靠 : 让使用者清楚地知道函数会返回什么。 (
function calculateTotal(...): number
) - 提供绝佳的编辑器智能提示,极大地提升开发效率和代码质量。
- 确保输入正确 : 防止调用者传入错误类型的数据。 (
4. 对象、接口 (Interfaces) 与 类型别名 (Type Aliases)
两者都可以描述对象结构,但有细微差别和侧重点。经验法则是:能用 interface
定义对象结构时就用 interface
,其他情况用 type
。
- 接口 (
interface
) :- 解释 : 为对象定义一个"契约"或"蓝图"。它可以被类
implements
(实现) 和其他接口extends
(继承),更符合面向对象的思想。 - 场景 : 优先使用
interface
来描述数据结构。比如 API 的响应数据、函数期望接收的对象参数等。 - 示例 :
interface Product { readonly id: number; name: string; price: number; description?: string; }
- 解释 : 为对象定义一个"契约"或"蓝图"。它可以被类
- 类型别名 (
type
) :- 解释: 为一个类型创建一个新的名字。它更通用,不仅能描述对象,还能为任何类型(如联合类型、字面量类型)创建别名。
- 场景 :
- 定义联合类型 : 当一个变量可以是多种类型之一时。
type StringOrNumber = string | number;
- 定义字面量类型 : 当变量的值只能是一组特定的字符串或数字时。
type Status = "loading" | "success" | "error";
- 为复杂的类型命名: 让你免于重复书写冗长的类型定义。
- 定义联合类型 : 当一个变量可以是多种类型之一时。
第二阶段:进阶概念 (让你的代码更灵活)
5. 联合类型 (Union Types) 与 交叉类型 (Intersection Types)
- 联合类型 (
|
) :- 解释: 表示一个值可以是几种类型之一。
- 场景: 当一个函数参数或变量可能接受不同类型的值时。
- 示例 :
function printId(id: number | string) { ... }
- 交叉类型 (
&
) :- 解释: 将多个类型合并为一个类型,新类型将拥有所有成员的特性。
- 场景: 当你需要组合多个已有的类型定义来创建一个更复杂的类型时。
- 示例 :
type AdminUser = User & { permissions: string[]; };
6. 类型断言 (Type Assertions) 与 类型守卫 (Type Guards)
- 类型断言 (
as
) :- 解释: 当你比 TypeScript 更清楚某个值的类型时,可以"告诉"编译器它的类型。这不会改变运行时的行为,仅仅是在编译时起作用。
- 场景 : 在处理
unknown
或any
类型后,你通过自己的逻辑检查确定了其具体类型。 - 示例 :
const name = (response as { name: string }).name;
- 警告: 不要滥用类型断言,它可能会隐藏真正的类型错误。
- 类型守卫 :
- 解释 : 在代码的某个分支中,通过逻辑判断来缩小变量的类型范围。
- 场景: 在处理联合类型时,需要根据具体类型执行不同逻辑。
- 示例 :
if (typeof id === 'string') { id.toUpperCase(); }
(使用typeof
) 或if (pet instanceof Dog) { pet.bark(); }
(使用instanceof
)。
7. 字面量类型 (Literal Types) 与 枚举 (Enums)
- 字面量类型 :
- 解释: 将变量的类型限制为某个或某几个具体的字符串或数字。它通常与联合类型一起使用。
- 场景 : 当函数的参数或变量只能接受几个特定的值时,可以提供比
string
或number
更精确的类型定义。 - 示例 :
function setAlign(align: "left" | "center" | "right") { ... }
- 枚举 (
enum
) :- 解释: 用于定义一组命名的常量集合。
- 场景: 当你有一组相关的常量,并希望给它们赋予一个语义化的名字时。
- 示例 :
enum Direction { Up, Down, Left, Right }
。使用Direction.Up
比使用数字0
更清晰易懂。
8. 类 (Classes)
- 解释 : TypeScript 对 ES6
class
的增强,加入了类型注解和访问修饰符。 - 修饰符 :
public
: (默认) 任何地方都可以访问。private
: 只能在类的内部访问。protected
: 只能在类及其子类的内部访问。readonly
: 属性只能在声明时或构造函数中被初始化,之后不可修改。
- 场景 : 在使用面向对象编程(OOP)范式来组织代码时,
class
是核心。
第三阶段:高级与泛型 (编写可重用的高质量代码)
9. 泛型 (Generics)
-
解释 : 泛型是 TypeScript 的一个核心特性,它允许你编写可重用的组件。你可以把它想象成一个类型的"占位符"或"变量",在你使用它时才传入具体的类型。
-
要解决的问题 : 避免为不同类型重复编写相同的逻辑,同时又不像
any
那样丢失类型信息。 -
示例:从问题到泛型
typescript// 尝试1: 使用 any,丢失了类型信息 function identityAny(arg: any): any { return arg; } const outputAny = identityAny("hello"); // outputAny 的类型是 any // 解决方案: 使用泛型 function identity<T>(arg: T): T { return arg; } const output = identity("hello"); // output 的类型被正确推断为 string
-
核心场景 :
-
1. 泛型函数 : 创建可重用的、类型安全的函数。
typescript// 场景:创建一个函数,它的输入和输出类型可以根据调用时传入的类型动态确定。 function createSuccessResponse<T>(data: T) { return { success: true, data: data }; } const userResponse = createSuccessResponse({ id: 1, name: "Alice" }); // userResponse.data.id 具有正确的类型提示
-
2. 泛型接口/类 : 创建可重用的数据结构。
typescript// 场景:定义一个分页查询的返回结果,其中列表项的类型是可变的。 interface PaginatedResult<T> { items: T[]; total: number; }
-
3. 泛型约束 (
extends
) : 限制泛型T
必须满足某些条件。typescript// 场景:确保传入的参数一定有 .length 属性。 function logLength<T extends { length: number }>(arg: T): void { console.log(arg.length); } logLength("hello"); // OK // logLength(123); // Error
-
10. 高级类型 (Advanced Types)
这些类型工具可以让你对已有类型进行变换、组合和提取。
-
keyof
(索引类型查询) : 获取一个类型的所有键 ,并返回一个由这些键组成的字符串字面量联合类型 。-
场景: 创建能够安全访问对象属性的函数,防止传入不存在的属性名。
-
示例 :
typescriptfunction getProperty<T, K extends keyof T>(obj: T, key: K) { return obj[key]; // key 被约束为 obj 中存在的键 }
-
-
typeof
(类型查询) : 在类型上下文 中,获取一个变量 或属性 的类型。-
场景: 当你想从一个已存在的 JavaScript 对象(如配置对象)中创建类型,而不想手动重复定义时。
-
示例 :
typescriptconst AppConfig = { apiPrefix: "/api", maxRetries: 3 }; type Config = typeof AppConfig; // { apiPrefix: string; maxRetries: number; }
-
-
Mapped Types (映射类型) : 基于一个已有的类型,通过规则转换 它的每一个属性,从而创建一个新类型。
-
语法 :
[P in K]: T
,类似 for...in 循环。 -
场景 : 这是许多内置工具类型(如
Readonly
,Partial
)的实现基础。 -
示例 (实现 Readonly) :
typescripttype MyReadonly<T> = { readonly [P in keyof T]: T[P]; };
-
-
Conditional Types (条件类型) : 让类型根据一个条件在两种类型中选择一种。
-
语法 :
SomeType extends OtherType ? TrueType : FalseType;
-
infer
关键字 : 在extends
条件中,可以捕获推断出的类型。 -
场景: 编写高级工具类型,能从其他类型中"提取"或"解包"出部分类型。
-
示例 (解包数组元素的类型) :
typescripttype Flatten<T> = T extends (infer Item)[] ? Item : T; type Str = Flatten<string[]>; // 最终类型是 string type Num = Flatten<number>; // 最终类型是 number
-
11. 工具类型 (Utility Types)
- 解释: TypeScript 内置了很多方便的、基于泛型和高级类型实现的工具类型,可以直接使用,极大地提高开发效率。
- 场景/常用示例 :
Partial<T>
: 将T
的所有属性变为可选。场景: 更新对象的部分属性时。Readonly<T>
: 将T
的所有属性变为只读。场景: 创建不可变数据。Pick<T, K>
: 从T
中挑选出K
属性来创建一个新类型。场景: 当你只需要一个大对象中的几个属性时。Omit<T, K>
: 从T
中排除掉K
属性,用剩下的属性创建新类型。场景 : 与Pick
相反。ReturnType<T>
: 获取函数类型T
的返回值类型。
第四阶段:工程实践 (将知识应用于真实项目)
12. 模块化 (Modules)
- 解释 : TypeScript 遵循 ES6 模块标准,使用
import
和export
来组织代码结构。 - 场景: 在任何非 "Hello World" 的项目中,你都应该使用模块化来拆分代码,保持代码的清晰和可维护性。
13. 声明文件 (Declaration Files)
- 解释 : 后缀为
.d.ts
的文件,它不包含实现,只包含类型定义。 - 场景 : 当你使用一个纯 JavaScript 编写的库时,它本身没有类型信息。为了在 TypeScript 项目中获得这个库的类型检查和智能提示,你需要为其提供一个声明文件。很多流行的库都有社区维护的
@types/...
包,可以直接安装。
14. tsconfig.json
深度解析
- 解释: TypeScript 编译器的配置文件,它告诉编译器如何检查和编译你的代码。
- 场景: 任何 TypeScript 项目的核心。
- 重要选项 :
"target"
: 编译后输出的 JavaScript 版本。"module"
: 编译后使用的模块系统。"strict"
: 开启所有严格类型检查选项,强烈建议始终开启。"paths"
: 设置路径别名,简化模块导入路径。"lib"
: 指定项目中可以使用的标准库。
学习建议:
- 扎实走好每一步:不要急于求成,确保理解了前一个概念再进入下一个。
- 多动手实践:在你的项目中为每个知识点编写一些小例子来加深理解。
- 保持好奇心:多查阅官方文档,遇到问题积极寻找解决方案。