第 6 章:类型别名与联合
学习目标
- 用 type 创建别名
- 学会联合类型(Union)与字面量类型
- 掌握交叉类型(Intersection)
- 区分 type 和 interface 的使用场景
一、类型别名(type alias)
typescript
// 给原始类型起别名
type ID = string | number;
type Callback = (result: number) => void;
// 给对象起别名(等同于 interface)
type User = {
id: number;
name: string;
};
// 给联合起别名
type Status = 'success' | 'error' | 'loading';
const id: ID = 'abc-123';
const cb: Callback = (r) => console.log(r);二、联合类型(Union)
一个变量可以是多种类型之一。
typescript
let value: string | number = 'Tom';
value = 123; // ✅
value = true; // ❌ Type 'boolean' is not assignable
// 函数参数联合
function print(id: string | number): void {
console.log(`ID: ${id}`);
}
print(1); // ✅
print('abc'); // ✅联合类型需要"收窄"
typescript
function print(id: string | number): void {
console.log(id.toUpperCase());
// ❌ Property 'toUpperCase' does not exist on type 'number'
}
function print(id: string | number): void {
if (typeof id === 'string') {
console.log(id.toUpperCase()); // ✅ 收窄为 string
} else {
console.log(id.toFixed(2)); // ✅ 收窄为 number
}
}⚠️ 坑 1:联合类型不能直接调"所有分支都有的方法",必须先
typeof/in/instanceof收窄。
三、字面量类型(Literal Types)
typescript
// 字符串字面量
type Direction = 'up' | 'down' | 'left' | 'right';
function move(d: Direction): void {
console.log(d);
}
move('up'); // ✅
move('side'); // ❌ Argument of type '"side"' is not assignable
// 数字字面量
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
const roll: Dice = 3;
// 布尔字面量
type Bool = true; // 只能是 true典型场景:
typescript
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Status = 200 | 404 | 500;
interface Request {
method: Method;
url: string;
}
const req: Request = { method: 'GET', url: '/api/users' };四、可辨识联合(Discriminated Union)
typescript
interface LoadingState {
status: 'loading';
}
interface SuccessState<T> {
status: 'success';
data: T;
}
interface ErrorState {
status: 'error';
error: string;
}
type State<T> = LoadingState | SuccessState<T> | ErrorState;
function render(state: State<User>): string {
switch (state.status) {
case 'loading':
return '加载中...';
case 'success':
return `数据:${state.data.name}`; // ✅ 收窄到 SuccessState
case 'error':
return `错误:${state.error}`; // ✅ 收窄到 ErrorState
}
}⚠️ 坑 2:
status这种字段叫辨识字段(discriminant),必须每种类型都有,且类型不同(常用字面量)。
五、交叉类型(Intersection)
类型 A 和 B 合并(&):
typescript
type A = { name: string };
type B = { age: number };
type AB = A & B; // { name: string; age: number }
const user: AB = { name: 'Tom', age: 18 };
// 函数交叉
type Fn1 = (a: number) => string;
type Fn2 = (b: string) => number;
type FnCombined = Fn1 & Fn2; // 两个签名都必须实现(其实几乎不可能,慎用)常见用法:
typescript
// 1. 混入(mixin)
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type Post = { title: string } & WithTimestamps;
// 2. 给已有类型加字段
type UserWithToken = User & { token: string };六、type 与 interface 的区别
| 维度 | type | interface |
|---|---|---|
| 对象/函数 | ✅ | ✅ |
| 联合/交叉 | ✅ | ❌ |
| 字面量 | ✅ | ❌ |
| 合并(declare merge) | ❌ | ✅ |
| 继承 | 用 & | 用 extends |
| implements | ✅ | ✅ |
typescript
// type 不能这样:
type User = { name: string };
type User = { age: number }; // ❌ Duplicate identifier 'User'
// interface 可以合并:
interface User { name: string; }
interface User { age: number; } // ✅ 自动合并七、实战:状态机
typescript
type State =
| { kind: 'idle' }
| { kind: 'loading' }
| { kind: 'success'; data: User[] }
| { kind: 'error'; msg: string };
function render(state: State): string {
switch (state.kind) {
case 'idle': return '未开始';
case 'loading': return '加载中';
case 'success': return `共 ${state.data.length} 条`;
case 'error': return `失败:${state.msg}`;
}
}八、本章小结
| 要点 | 关键 |
|---|---|
| 类型别名 | type ID = string | number |
| 联合类型 | string | number,需收窄才能用 |
| 字面量 | 'GET' | 'POST' 限定取值范围 |
| 可辨识联合 | 每种 case 带不同字面量字段,switch 收窄 |
| 交叉类型 | A & B,合并所有字段 |
| type vs interface | 对象结构用 interface,联合/字面量用 type |
动手练习
- 联合类型:写
format(value: string \| number),字符串加引号、数字保留 2 位小数 - 字面量状态:用字面量联合定义
TrafficLight = 'red' \| 'yellow' \| 'green' - 可辨识联合:实现
handleEvent(e: ClickEvent \| KeyEvent \| ScrollEvent),根据kind区分
下一章:第 7 章:泛型 →