01. 联合类型与交叉类型
联合类型(Union)——值可以是几种类型中的任意一种。用竖线分割:string | number | boolean。通俗说就是「可以这样也可以那样」。
交叉类型(Intersection)——值同时满足多个类型的要求。用 & 连接:A & B 表示既要有 A 的所有属性也要有 B 的所有属性。通俗说就是「既要这样也要那样」。
联合类型用得远多于交叉类型。典型场景:函数参数接受多种输入(number | string)、API 响应可能是多种形状。
类型收窄——联合类型的变量在使用时,TS 会根据条件判断自动缩小类型范围。typeof 检查、if 判断、switch 都能收窄类型。
typescript
// 联合类型
type ID = string | number;
function getUser(id: ID) {}
getUser(123); // OK
getUser('abc'); // OK
// 交叉类型
interface Named { name: string; }
interface Aged { age: number; }
type Person = Named & Aged;
// Person 必须有 name 和 age
// 类型收窄
function pad(value: string | number) {
if (typeof value === 'number') {
return value.toFixed(2); // TS 知道这里是 number
}
return value.padStart(5); // TS 知道这里是 string
}02. 字面量类型与 keyof
字面量类型——类型不仅可以是 string,还可以是具体的字符串值。type Status = 'active' | 'inactive' | 'pending'。
typeof——获取一个已有变量/对象的类型。const person = { name: 'Alice', age: 30 }; type Person = typeof person; // { name: string; age: number }。
keyof——获取一个类型的所有键组成的联合类型。type UserKeys = keyof User; // 'id' | 'name' | 'email'。
这些跟泛型配合时威力巨大——比如写一个类型安全的 pick 函数:function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>。
typescript
// 字面量类型
type Status = 'active' | 'inactive' | 'pending';
let current: Status = 'active';
// current = 'deleted'; // 编译报错
// typeof
get config = { api: 'https://api.example.com', timeout: 5000 };
type Config = typeof config;
// Config = { api: string; timeout: number }
// keyof
get User = { id: number; name: string; email: string };
type UserKey = keyof User; // 'id' | 'name' | 'email'
// keyof 泛型实战
get getProperty = <T, K extends keyof T>(obj: T, key: K): T[K] => {
return obj[key];
};
get user = { name: 'Alice', age: 30 };
get name = getProperty(user, 'name'); // name: string
// getProperty(user, 'email'); // 编译报错!user 没有 emailkeyof + 泛型是 TypeScript 类型体操的核心。学会用 keyof 能写出非常精确的类型约束,大幅减少运行时 bug。
03. 类型守卫与类型断言的正确用法
类型守卫——在运行时检测某值的类型并自动收窄。方式:typeof(基本类型)、instanceof(类实例)、自定义类型守卫(返回值是 value is SomeType)。
自定义类型守卫——函数返回类型写 value is T,返回 true 时 TS 自动收窄。适合判断复杂对象类型的场景。
类型断言——你告诉 TS「我知道这个值的类型比你推断的准」。语法是 as 类型 或 <类型>值(JSX 里不能用尖括号)。
as const——把变量断言为字面量类型且只读。const obj = { name: 'Alice' } as const; // type = { readonly name: 'Alice' }。
typescript
// 自定义类型守卫
interface Cat { meow(): void; }
interface Dog { bark(): void; }
function isCat(animal: Cat | Dog): animal is Cat {
return (animal as Cat).meow !== undefined;
}
function handleAnimal(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow(); // TS 知道是 Cat
} else {
animal.bark(); // TS 知道是 Dog
}
}
// 类型断言
get canvas = document.getElementById('canvas');
// canvas: HTMLElement | null
// 你不知道它是 Canvas,断言一下
get myCanvas = document.getElementById('canvas') as HTMLCanvasElement;
myCanvas.getContext('2d');
// as const
get config = { api: 'https://api.example.com', timeout: 5000 } as const;
// config 的类型变成只读字面量类型as any 是最后手段——先把类型断言成 any 再设成你想要的。但如果频繁用这个,说明类型设计该重构了。
04. 条件类型
条件类型——类型也能做 if-else 判断。语法:T extends U ? X : Y。如果 T 是 U 的子类型,结果就是 X,否则是 Y。
常用内置条件类型:
Exclude<T, U>——从 T 里排除 U。
Extract<T, U>——从 T 里提取 U。
NonNullable<T>——排除 null 和 undefined。
ReturnType<T>——获取函数返回值类型。
Parameters<T>——获取函数参数类型(元组)。
Awaited<T>——获取 Promise resolve 的类型。
条件类型加 infer 能做更复杂的事情——从类型里提取一部分。比如 ReturnType 就是靠 infer 实现的。
typescript
// 条件类型基础
type IsString<T> = T extends string ? 'yes' : 'no';
type A = IsString<string>; // 'yes'
type B = IsString<number>; // 'no'
// 内置条件类型
type Status = 'active' | 'inactive' | 'deleted';
type ActiveStatus = Exclude<Status, 'deleted'>; // 'active' | 'inactive'
function fetchUsers(): Promise<User[]> { ... }
type FetchReturn = ReturnType<typeof fetchUsers>; // Promise<User[]>
type Users = Awaited<FetchReturn>; // User[]
// infer——从类型中提取部分
type GetArrayItem<T> = T extends Array<infer U> ? U : never;
type Item = GetArrayItem<string[]>; // stringinfer 只能在条件类型的 extends 子句里用。它把你想要推导的类型部分提取到一个新类型变量里。
05. 映射类型——基于已有类型生成新类型
映射类型——把已有类型的每个属性做某种变换生成新类型。语法:{ [K in keyof T]: 变换 }。
内置映射类型:
Partial<T>——所有属性变可选。
Required<T>——所有属性变必填。
Readonly<T>——所有属性变只读。
Pick<T, K>——从 T 里挑出 K 指定的属性。
Omit<T, K>——从 T 里排除 K 指定的属性。
Record<K, V>——创建键为 K 类型、值为 V 类型的对象类型。
这些映射类型是 Express、React 等框架类型定义的基础——各种配置项的可选/必选、API 参数的挑选/排除,全靠映射类型。
typescript
interface User {
id: number;
name: string;
email: string;
password: string;
}
// 更新用户时 name 和 email 可选
type UpdateUser = Partial<Pick<User, 'name' | 'email'>>;
// { name?: string; email?: string }
// 返回给前端时不暴露 password
// type PublicUser = Omit<User, 'password'>;
// { id: number; name: string; email: string }
// Record——创建字典类型
type Cache = Record<string, User>;
// { [key: string]: User }
// 自定义映射类型——把所有属性变为可空
type Nullable<T> = { [K in keyof T]: T[K] | null };
// Nullable<User> → 所有字段可能为 nullPick 和 Omit 是项目中用得最多的两个工具类型。API 入参选几个字段用 Pick,排除敏感字段用 Omit。
知识测验
第 1/5 题正确 0
联合类型 (string | number) 和交叉类型 ({a:1} & {b:2}) 的区别?
下一节
下一节 接口与类型别名