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