本文献给:
已掌握 TypeScript 基础类型(string、number、boolean、数组、元组、枚举)及特殊类型(any、unknown、void、never、null、undefined)的开发者。本文将带你学习如何通过类型别名、字面量类型、联合类型和交叉类型来构建更灵活、更精准的类型表达。
你将学到:
- 类型别名
type的定义与使用场景 - 字符串字面量、数字字面量类型的含义
as const如何解决字面量推断问题- 联合类型
|的组合方式与属性访问限制 - 交叉类型
&的合并特性
目录
- [一、类型别名(Type Aliases)](#一、类型别名(Type Aliases))
-
- [1.1 基本语法](#1.1 基本语法)
- [1.2 对象类型别名](#1.2 对象类型别名)
- [1.3 联合类型别名](#1.3 联合类型别名)
- [1.4 类型别名 vs 接口](#1.4 类型别名 vs 接口)
- [二、字面量类型(Literal Types)](#二、字面量类型(Literal Types))
-
- [2.1 字符串字面量类型](#2.1 字符串字面量类型)
- [2.2 数字字面量类型](#2.2 数字字面量类型)
- [2.3 布尔字面量类型](#2.3 布尔字面量类型)
- [2.4 字面量推断问题](#2.4 字面量推断问题)
- [三、联合类型(Union Types)](#三、联合类型(Union Types))
-
- [3.1 基本用法](#3.1 基本用法)
- [3.2 函数参数中的联合](#3.2 函数参数中的联合)
- [3.3 属性访问限制](#3.3 属性访问限制)
- [3.4 字面量联合的优势](#3.4 字面量联合的优势)
- [四、交叉类型(Intersection Types)](#四、交叉类型(Intersection Types))
-
- [4.1 基本用法](#4.1 基本用法)
- [4.2 合并冲突](#4.2 合并冲突)
- [4.3 对象扩展场景](#4.3 对象扩展场景)
- [4.4 交叉类型与联合类型的区别](#4.4 交叉类型与联合类型的区别)
- [五、as const 深入](#五、as const 深入)
-
- [5.1 对象字面量的 as const](#5.1 对象字面量的 as const)
- [5.2 数组的 as const](#5.2 数组的 as const)
- [5.3 as const 与枚举对比](#5.3 as const 与枚举对比)
- 六、常见场景与示例
-
- [6.1 类型别名 + 字面量联合定义状态](#6.1 类型别名 + 字面量联合定义状态)
- [6.2 联合类型与交叉类型的组合](#6.2 联合类型与交叉类型的组合)
- [6.3 as const 构建常量对象](#6.3 as const 构建常量对象)
- [6.4 交叉类型扩展已有类型](#6.4 交叉类型扩展已有类型)
- 七、常见错误与注意事项
-
- [7.1 联合类型直接访问不存在属性](#7.1 联合类型直接访问不存在属性)
- [7.2 交叉类型过度使用导致难以理解](#7.2 交叉类型过度使用导致难以理解)
- [7.3 as const 与 let 的配合](#7.3 as const 与 let 的配合)
- 八、综合示例
- 九、小结
一、类型别名(Type Aliases)
1.1 基本语法
类型别名用来为任意类型起一个新名字,使用 type 关键字定义。
typescript
type UserID = string;
type Age = number;
let id: UserID = "abc123";
let age: Age = 25;
类型别名可以描述复杂类型,让代码更清晰。
1.2 对象类型别名
常用于定义对象的结构:
typescript
type Point = {
x: number;
y: number;
};
function distance(p1: Point, p2: Point): number {
return Math.hypot(p1.x - p2.x, p1.y - p2.y);
}
1.3 联合类型别名
类型别名也可以用来给联合类型命名:
typescript
type Status = "pending" | "success" | "error";
let currentStatus: Status = "pending";
1.4 类型别名 vs 接口
| 特性 | 类型别名 type |
接口 interface |
|---|---|---|
| 定义对象 | ✅ | ✅ |
| 定义联合 | ✅ | ❌ |
| 定义元组 | ✅ | ❌ |
| 合并声明 | ❌(不可重复定义同名) | ✅(同名接口自动合并) |
| 扩展方式 | & 交叉类型 |
extends 关键字 |
两者各有侧重,通常对象形状优先用 interface,联合、元组等场景用 type。
二、字面量类型(Literal Types)
2.1 字符串字面量类型
字符串字面量类型允许将字符串的具体值作为类型,类似于枚举但更轻量。
typescript
let direction: "left" | "right" | "up" | "down";
direction = "left"; // OK
direction = "top"; // ❌ 类型 "top" 不能赋给 "left" | "right" | "up" | "down"
2.2 数字字面量类型
同理,数字的具体值也可以作为类型:
typescript
let dice: 1 | 2 | 3 | 4 | 5 | 6;
dice = 3; // OK
dice = 7; // ❌
2.3 布尔字面量类型
虽然很少单独用,但 true 和 false 本身就是字面量类型。
typescript
let isTrue: true = true;
isTrue = false; // ❌
2.4 字面量推断问题
当使用 const 声明时,TypeScript 会推断出最精确的字面量类型:
typescript
const a = "hello"; // 类型为 "hello"
let b = "hello"; // 类型为 string
但如果用 let 或 var,类型会被拓宽为基本类型。有时我们希望保留字面量类型,可以使用 as const。
typescript
let c = "hello" as const; // 类型为 "hello"
let d = ["x", "y"] as const; // 类型为 readonly ["x", "y"]
as const 将整个对象或数组变为只读,并推断为最具体的字面量类型。
typescript
const config = {
theme: "dark",
size: 100
} as const;
// config 的类型为:
// {
// readonly theme: "dark";
// readonly size: 100;
// }
三、联合类型(Union Types)
3.1 基本用法
联合类型使用 | 分隔,表示值可以是多种类型中的一种。
typescript
let value: string | number;
value = "hello"; // OK
value = 42; // OK
value = true; // ❌ 不能赋 boolean
3.2 函数参数中的联合
常用于接受多种类型的参数:
typescript
function format(input: string | number): string {
return `Result: ${input}`;
}
3.3 属性访问限制
当使用联合类型时,只能访问所有类型共有的属性或方法。
typescript
interface Dog {
name: string;
bark(): void;
}
interface Cat {
name: string;
meow(): void;
}
type Pet = Dog | Cat;
function greet(pet: Pet) {
console.log(pet.name); // OK,name 是共有属性
pet.bark(); // ❌ 不是所有 Pet 都有 bark
}
要使用特定类型的特有属性,需要先通过类型守卫收窄类型。
3.4 字面量联合的优势
字面量联合类型常用于替代枚举,更轻量且无运行时开销。
typescript
type Theme = "light" | "dark" | "auto";
let currentTheme: Theme = "light";
四、交叉类型(Intersection Types)
4.1 基本用法
交叉类型使用 & 操作符,将多个类型合并为一个,新类型将拥有所有类型的成员。
typescript
type Name = {
name: string;
};
type Age = {
age: number;
};
type Person = Name & Age;
let p: Person = {
name: "Alice",
age: 25
};
4.2 合并冲突
当交叉的类型有同名但类型不同的属性时,该属性会变成 never,通常意味着设计上需要调整。
typescript
type A = { id: number };
type B = { id: string };
type C = A & B; // id 的类型为 number & string → never
// 无法构造满足 C 的对象
4.3 对象扩展场景
交叉类型常用于组合多个对象类型,类似接口的继承:
typescript
type Base = { id: string };
type Extended = Base & { createdAt: Date };
4.4 交叉类型与联合类型的区别
- 联合类型(
|):表示"或",值是其中一种类型。 - 交叉类型(
&):表示"且",值必须同时满足所有类型。
五、as const 深入
5.1 对象字面量的 as const
as const 告诉 TypeScript 将对象中的属性推断为字面量类型,并将所有属性设为 readonly。
typescript
const routes = {
home: "/",
user: "/user",
admin: "/admin"
} as const;
// routes 的类型为:
// {
// readonly home: "/";
// readonly user: "/user";
// readonly admin: "/admin";
// }
5.2 数组的 as const
数组用 as const 后会变成只读元组,元素类型为字面量。
typescript
const sizes = ["small", "medium", "large"] as const;
// 类型为 readonly ["small", "medium", "large"]
type Size = typeof sizes[number]; // "small" | "medium" | "large"
这种模式常用于从数组定义联合类型,避免重复书写。
5.3 as const 与枚举对比
as const 结合数组或对象可以模拟枚举的行为,且没有运行时对象开销:
typescript
// 模拟枚举
const StatusEnum = {
Pending: "pending",
Success: "success",
Error: "error"
} as const;
type Status = typeof StatusEnum[keyof typeof StatusEnum]; // "pending" | "success" | "error"
六、常见场景与示例
6.1 类型别名 + 字面量联合定义状态
typescript
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
function request(url: string, method: HttpMethod) {
// ...
}
6.2 联合类型与交叉类型的组合
typescript
type Success = { data: string; code: 200 };
type Error = { error: string; code: 400 | 500 };
type ApiResponse = Success | Error;
function handle(response: ApiResponse) {
if (response.code === 200) {
console.log(response.data); // 收窄为 Success
} else {
console.log(response.error); // 收窄为 Error
}
}
6.3 as const 构建常量对象
typescript
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
} as const;
// 可以获取字面量类型
type Config = typeof config;
// {
// readonly apiUrl: "https://api.example.com";
// readonly timeout: 5000;
// readonly retries: 3;
// }
6.4 交叉类型扩展已有类型
typescript
type Timestamped = {
createdAt: Date;
updatedAt: Date;
};
type User = {
id: string;
name: string;
};
type TimestampedUser = User & Timestamped;
const user: TimestampedUser = {
id: "1",
name: "Alice",
createdAt: new Date(),
updatedAt: new Date()
};
七、常见错误与注意事项
7.1 联合类型直接访问不存在属性
typescript
type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };
function area(shape: Shape) {
return shape.radius; // ❌ 属性 radius 不存在于所有 Shape
}
必须使用类型守卫(如 if (shape.kind === "circle"))收窄。
7.2 交叉类型过度使用导致难以理解
过多的交叉类型会让类型变得复杂,必要时可以拆分为多个小类型别名。
7.3 as const 与 let 的配合
as const 配合 let 时,变量类型是字面量,但仍然可以被重新赋值(只要类型匹配)。
typescript
let direction = "left" as const; // 类型为 "left"
direction = "right"; // ❌ 类型 "right" 不能赋给 "left"
如果需要可变但保留字面量约束,应使用类型注解联合类型。
八、综合示例
typescript
// 定义角色权限的类型别名
type Role = "admin" | "editor" | "viewer";
// 定义用户基础信息
type UserBase = {
id: string;
name: string;
};
// 定义带角色的用户
type User = UserBase & {
role: Role;
permissions?: string[];
};
// 使用 as const 定义权限列表
const allPermissions = ["read", "write", "delete"] as const;
type Permission = typeof allPermissions[number]; // "read" | "write" | "delete"
// 角色权限映射
type RolePermissions = {
[K in Role]: Permission[];
};
const rolePermissions: RolePermissions = {
admin: ["read", "write", "delete"],
editor: ["read", "write"],
viewer: ["read"]
};
// 检查用户权限
function hasPermission(user: User, permission: Permission): boolean {
if (user.role === "admin") return true;
const allowed = rolePermissions[user.role];
return allowed.includes(permission);
}
// 使用
const currentUser: User = {
id: "u1",
name: "Alice",
role: "editor"
};
console.log(hasPermission(currentUser, "write")); // true
console.log(hasPermission(currentUser, "delete")); // false
九、小结
| 概念 | 语法示例 | 说明 |
|---|---|---|
| 类型别名 | type ID = string |
为任意类型起名字 |
| 字符串字面量 | `type Dir = "left" | "right"` |
| 数字字面量 | `type Dice = 1 | 2 |
| as const | const obj = { a: 1 } as const |
推断为字面量只读类型 |
| 联合类型 | `type T = A | B` |
| 交叉类型 | type T = A & B |
值必须同时满足 A 和 B |
| 属性访问限制 | 联合类型只能访问共有成员 | 需类型守卫收窄 |
觉得文章有帮助?别忘了:
👍 点赞 👍 -- 给我一点鼓励
⭐ 收藏 ⭐ -- 方便以后查看
🔔 关注 🔔 -- 获取更新通知
标签: #TypeScript #类型别名 #字面量类型 #联合类型 #交叉类型 #前端开发