类型别名
概述
类型别名(Type Alias)是 TypeScript 中为类型创建新名称的方式。通过 type 关键字,我们可以为任何类型(包括基础类型、联合类型、交叉类型、函数类型等)创建一个别名,使代码更加清晰、可读和可维护。类型别名提供了比接口更灵活的类型定义方式,特别适合定义联合类型、交叉类型和复杂的类型组合。
基本语法
类型别名使用 type 关键字定义,后跟别名名称、等号和类型定义:
typescript
// 基本语法
type AliasName = Type;
// 示例:为字符串类型创建别名
type UserName = string;
type UserAge = number;
// 使用类型别名
const name: UserName = "John Doe";
const age: UserAge = 30;基本用法
为对象类型创建别名
类型别名最常见的用法是为对象类型创建别名:
typescript
// 使用类型别名定义对象类型
type Point = {
x: number;
y: number;
};
// 使用类型别名
const point: Point = {
x: 10,
y: 20
};
// 函数参数和返回值
function calculateDistance(p1: Point, p2: Point): number {
const dx = p1.x - p2.x;
const dy = p1.y - p2.y;
return Math.sqrt(dx * dx + dy * dy);
}
const distance = calculateDistance(
{ x: 0, y: 0 },
{ x: 3, y: 4 }
); // 5为联合类型创建别名
类型别名非常适合定义联合类型(Union Types):
typescript
// 定义状态类型
type Status = "pending" | "success" | "error";
// 使用状态类型
function handleStatus(status: Status): void {
switch (status) {
case "pending":
console.log("处理中...");
break;
case "success":
console.log("成功");
break;
case "error":
console.log("错误");
break;
}
}
// ✅ 正确
handleStatus("pending");
handleStatus("success");
// ❌ 错误:类型不匹配
// handleStatus("unknown"); // Error: Type '"unknown"' is not assignable to type 'Status'为交叉类型创建别名
类型别名也可以用于定义交叉类型(Intersection Types):
typescript
// 定义基础类型
type Person = {
name: string;
age: number;
};
type Employee = {
employeeId: string;
department: string;
};
// 使用交叉类型组合多个类型
type EmployeePerson = Person & Employee;
// EmployeePerson 包含 Person 和 Employee 的所有属性
const employee: EmployeePerson = {
name: "Alice",
age: 30,
employeeId: "E001",
department: "Engineering"
};为函数类型创建别名
类型别名可以定义函数类型:
typescript
// 定义函数类型别名
type MathOperation = (a: number, b: number) => number;
// 使用函数类型别名
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;
const multiply: MathOperation = (a, b) => a * b;
// 函数参数使用函数类型
function calculate(
operation: MathOperation,
x: number,
y: number
): number {
return operation(x, y);
}
console.log(calculate(add, 5, 3)); // 8
console.log(calculate(subtract, 5, 3)); // 2
console.log(calculate(multiply, 5, 3)); // 15为元组类型创建别名
类型别名可以定义元组类型:
typescript
// 定义元组类型别名
type Coordinate = [number, number];
type RGB = [number, number, number];
// 使用元组类型
const point: Coordinate = [10, 20];
const color: RGB = [255, 128, 0];
// 函数返回元组
function getCoordinates(): Coordinate {
return [0, 0];
}
// 解构元组
const [x, y] = getCoordinates();高级用法
泛型类型别名
类型别名支持泛型,可以创建可复用的类型定义:
typescript
// 定义泛型类型别名
type Container<T> = {
value: T;
isEmpty: boolean;
};
// 使用泛型类型别名
const stringContainer: Container<string> = {
value: "Hello",
isEmpty: false
};
const numberContainer: Container<number> = {
value: 42,
isEmpty: false
};
// 泛型函数使用泛型类型别名
function createContainer<T>(value: T): Container<T> {
return {
value,
isEmpty: false
};
}
const container = createContainer("TypeScript");条件类型别名
类型别名可以结合条件类型使用:
typescript
// 定义条件类型别名
type NonNullable<T> = T extends null | undefined ? never : T;
// 使用条件类型
type StringType = NonNullable<string | null>; // string
type NumberType = NonNullable<number | undefined>; // number
// 实际应用:提取数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type StringArrayElement = ArrayElement<string[]>; // string
type NumberArrayElement = ArrayElement<number[]>; // number映射类型别名
类型别名可以结合映射类型使用:
typescript
// 定义映射类型别名
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 使用映射类型
type User = {
name: string;
age: number;
email: string;
};
type ReadonlyUser = Readonly<User>;
// 等同于:
// {
// readonly name: string;
// readonly age: number;
// readonly email: string;
// }
type PartialUser = Partial<User>;
// 等同于:
// {
// name?: string;
// age?: number;
// email?: string;
// }递归类型别名
类型别名支持递归定义,可以表示树形结构等复杂类型:
typescript
// 定义递归类型别名:树节点
type TreeNode<T> = {
value: T;
children?: TreeNode<T>[];
};
// 使用递归类型
const tree: TreeNode<string> = {
value: "root",
children: [
{
value: "child1",
children: [
{ value: "grandchild1" },
{ value: "grandchild2" }
]
},
{
value: "child2"
}
]
};
// 递归类型:JSON 值
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue };
const jsonData: JSONValue = {
name: "John",
age: 30,
tags: ["developer", "typescript"],
metadata: {
active: true,
scores: [95, 87, 92]
}
};类型别名与接口的区别
类型别名和接口在很多场景下可以互换使用,但它们有一些重要区别:
1. 声明合并(Declaration Merging)
接口支持声明合并,类型别名不支持:
typescript
// 接口:可以多次声明,会自动合并
interface Window {
title: string;
}
interface Window {
width: number;
}
// 合并后的 Window 包含 title 和 width
const window: Window = {
title: "My Window",
width: 800
};
// 类型别名:不能重复声明
type Window = {
title: string;
};
// ❌ 错误:重复声明类型别名
// type Window = {
// width: number;
// }; // Error: Duplicate identifier 'Window'2. 扩展方式
接口使用 extends,类型别名使用交叉类型 &:
typescript
// 接口扩展
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// 类型别名扩展(使用交叉类型)
type AnimalType = {
name: string;
};
type DogType = AnimalType & {
breed: string;
};3. 适用场景
接口更适合:
- 定义对象结构
- 需要声明合并的场景
- 与类一起使用(
implements)
类型别名更适合:
- 定义联合类型
- 定义交叉类型
- 定义函数类型
- 定义元组类型
- 定义复杂的类型组合
typescript
// 接口:适合对象结构
interface User {
name: string;
age: number;
}
// 类型别名:适合联合类型
type Status = "active" | "inactive" | "pending";
// 类型别名:适合函数类型
type EventHandler = (event: Event) => void;
// 类型别名:适合复杂组合
type ApiResponse<T> =
| { status: "success"; data: T }
| { status: "error"; message: string };使用示例
示例 1:API 响应类型
typescript
// 定义 API 响应类型
type ApiResponse<T> =
| { status: "success"; data: T; code: 200 }
| { status: "error"; message: string; code: number };
// 使用泛型类型别名
type UserResponse = ApiResponse<{
id: number;
name: string;
email: string;
}>;
// 处理响应
function handleResponse<T>(response: ApiResponse<T>): void {
if (response.status === "success") {
console.log("Success:", response.data);
} else {
console.log("Error:", response.message, response.code);
}
}
// 创建响应对象
const successResponse: UserResponse = {
status: "success",
data: {
id: 1,
name: "John",
email: "john@example.com"
},
code: 200
};
const errorResponse: UserResponse = {
status: "error",
message: "User not found",
code: 404
};
handleResponse(successResponse); // ✅ 正确
handleResponse(errorResponse); // ✅ 正确示例 2:事件处理系统
typescript
// 定义事件类型
type EventType = "click" | "hover" | "focus" | "blur";
// 定义事件处理器类型
type EventHandler<T extends EventType> = (event: {
type: T;
target: HTMLElement;
timestamp: number;
}) => void;
// 定义事件映射类型
type EventMap = {
click: EventHandler<"click">;
hover: EventHandler<"hover">;
focus: EventHandler<"focus">;
blur: EventHandler<"blur">;
};
// 事件管理器
class EventManager {
private handlers: Partial<EventMap> = {};
on<T extends EventType>(type: T, handler: EventHandler<T>): void {
this.handlers[type] = handler as EventMap[T];
}
emit<T extends EventType>(type: T, event: Parameters<EventHandler<T>>[0]): void {
const handler = this.handlers[type];
if (handler) {
handler(event as any);
}
}
}
// 使用示例
const manager = new EventManager();
manager.on("click", (event) => {
console.log("Clicked:", event.target, event.timestamp);
});
manager.emit("click", {
type: "click",
target: document.body,
timestamp: Date.now()
});示例 3:状态管理类型
typescript
// 定义状态类型
type LoadingState = {
status: "loading";
};
type SuccessState<T> = {
status: "success";
data: T;
};
type ErrorState = {
status: "error";
error: string;
};
// 使用联合类型定义状态
type AsyncState<T> = LoadingState | SuccessState<T> | ErrorState;
// 使用示例
type User = {
id: number;
name: string;
};
type UserState = AsyncState<User>;
// 状态处理函数
function handleUserState(state: UserState): void {
switch (state.status) {
case "loading":
console.log("加载中...");
break;
case "success":
console.log("用户数据:", state.data);
break;
case "error":
console.log("错误:", state.error);
break;
}
}
// 创建不同状态
const loadingState: UserState = { status: "loading" };
const successState: UserState = {
status: "success",
data: { id: 1, name: "John" }
};
const errorState: UserState = {
status: "error",
error: "Failed to load user"
};
handleUserState(loadingState); // "加载中..."
handleUserState(successState); // "用户数据: { id: 1, name: 'John' }"
handleUserState(errorState); // "错误: Failed to load user"示例 4:工具函数类型
typescript
// 定义工具函数类型
type Predicate<T> = (item: T) => boolean;
type Mapper<T, U> = (item: T) => U;
type Reducer<T, U> = (accumulator: U, item: T) => U;
// 使用类型别名定义工具函数
function filter<T>(array: T[], predicate: Predicate<T>): T[] {
return array.filter(predicate);
}
function map<T, U>(array: T[], mapper: Mapper<T, U>): U[] {
return array.map(mapper);
}
function reduce<T, U>(
array: T[],
reducer: Reducer<T, U>,
initialValue: U
): U {
return array.reduce(reducer, initialValue);
}
// 使用示例
const numbers = [1, 2, 3, 4, 5];
// 过滤偶数
const evens = filter(numbers, (n) => n % 2 === 0); // [2, 4]
// 映射为字符串
const strings = map(numbers, (n) => n.toString()); // ["1", "2", "3", "4", "5"]
// 求和
const sum = reduce(numbers, (acc, n) => acc + n, 0); // 15类型检查示例
常见错误
typescript
// ❌ 错误:类型别名不能重复声明
type User = {
name: string;
};
// type User = {
// age: number;
// }; // Error: Duplicate identifier 'User'
// ❌ 错误:联合类型值不匹配
type Status = "active" | "inactive";
const status: Status = "pending"; // Error: Type '"pending"' is not assignable to type 'Status'
// ❌ 错误:缺少必需属性
type Person = {
name: string;
age: number;
};
const person: Person = {
name: "John"
// Error: Property 'age' is missing in type '{ name: string; }'
};正确写法
typescript
// ✅ 正确:使用类型别名定义对象
type User = {
name: string;
age: number;
email?: string; // 可选属性
};
const user: User = {
name: "John",
age: 30
};
// ✅ 正确:使用联合类型
type Status = "active" | "inactive" | "pending";
const status: Status = "pending"; // ✅ 正确
// ✅ 正确:使用交叉类型扩展
type BaseUser = {
name: string;
age: number;
};
type AdminUser = BaseUser & {
role: "admin";
permissions: string[];
};
const admin: AdminUser = {
name: "Alice",
age: 30,
role: "admin",
permissions: ["read", "write", "delete"]
};注意事项
提示
- 类型别名提供了比接口更灵活的类型定义方式,特别适合定义联合类型、交叉类型和复杂类型组合
- 使用类型别名可以提高代码的可读性和可维护性,特别是对于复杂的类型定义
- 类型别名支持泛型,可以创建可复用的类型定义
- 类型别名可以递归定义,适合表示树形结构等复杂数据结构
注意
- 类型别名不支持声明合并(Declaration Merging),如果需要声明合并,应该使用接口
- 类型别名是类型级别的,不会生成运行时代码,只在编译时进行类型检查
- 复杂的类型别名可能难以理解,建议使用有意义的名称并添加注释
- 类型别名不能使用
implements或extends关键字,需要使用交叉类型&来扩展
信息
类型别名在以下场景特别有用:
- 定义联合类型和交叉类型
- 定义函数类型和回调函数类型
- 定义复杂的类型组合和工具类型
- 创建可复用的泛型类型定义
- 定义递归类型结构
重要
- 类型别名和接口各有优势,根据具体场景选择合适的方式
- 对于对象结构,接口和类型别名都可以使用,但接口更适合需要声明合并的场景
- 对于联合类型、交叉类型等复杂类型,类型别名是唯一选择
- 理解类型别名与接口的区别,有助于在合适的场景选择合适的方式