1. 理解Union Type的核心概念
第一次在TypeScript里看到union type(联合类型)时,我正尝试为一个电商系统的商品属性建模。有些商品有固定规格(如手机内存64G/128G),有些则是自定义属性(如T恤颜色可填写任意值)。传统OOP的多态继承在这里显得笨重,而union type用一行代码type Memory = '64G' | '128G' | '256G'就完美解决了离散值的类型约束问题。
联合类型的本质是类型系统的逻辑或运算,它允许一个值属于多种类型中的一种。与any的彻底放飞不同,union type在灵活性和类型安全间取得了精妙平衡。当看到VSCode能智能提示出所有可能的内存选项时,我意识到这才是类型系统该有的样子。
2. 联合类型的声明与基础使用
2.1 类型声明语法
联合类型通过|运算符组合多个类型:
typescript复制type Status = 'pending' | 'success' | 'error';
let currentStatus: Status = 'pending'; // 只能赋值这三个字符串之一
实践中我发现几个有用的模式:
- 字面量联合:
type Direction = 'up' | 'down' - 类型联合:
type ID = string | number - 复杂类型联合:
type Shape = Circle | Square
2.2 类型收窄(Type Narrowing)
这是联合类型最强大的特性。当我们需要处理特定类型时,TypeScript会根据条件自动收窄类型范围:
typescript复制function printId(id: number | string) {
if (typeof id === 'string') {
console.log(id.toUpperCase()); // 这里id被识别为string
} else {
console.log(id.toFixed(2)); // 这里id被识别为number
}
}
我常用的收窄手段包括:
typeof类型守卫instanceof类判断- 自定义类型谓词(type predicate)
- 可辨识联合(discriminated union)
3. 高级联合类型技巧
3.1 可辨识联合模式
这是我处理复杂业务逻辑的利器。通过公共字段区分不同类型:
typescript复制type NetworkState =
| { state: 'loading' }
| { state: 'success', response: string }
| { state: 'failed', error: Error };
function handleState(state: NetworkState) {
switch (state.state) {
case 'loading':
showSpinner();
break;
case 'success':
console.log(state.response); // 能安全访问response
break;
case 'failed':
console.error(state.error.message); // 能安全访问error
break;
}
}
3.2 联合类型与泛型结合
在开发通用工具函数时,这种组合特别有用:
typescript复制function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
const user = merge(
{ name: 'Alice' },
{ age: 30 }
); // 类型推断为 { name: string } & { age: number }
4. 实战中的注意事项
4.1 过度联合的陷阱
早期我曾滥用联合类型导致类型系统失去意义:
typescript复制// 反面教材:过度宽泛的联合
type WeakType = string | number | boolean | object | any[] | null | undefined;
应该遵循最小化原则,只包含确实需要的类型。
4.2 性能考量
大型联合类型(超过100个成员)会影响编译器性能。在开发Monaco编辑器插件时,我发现包含500+字符串字面量的联合类型使代码补全变慢。解决方案是改用类型派生或枚举。
4.3 与交叉类型的配合
理解|和&的区别很重要:
typescript复制type A = { name: string };
type B = { age: number };
type C = A | B; // 要么有name,要么有age,或者两者都有
type D = A & B; // 必须同时有name和age
5. 与其他语言特性的协同
5.1 类型别名与接口
联合类型更常与type alias配合,而接口更适合用交叉类型扩展:
typescript复制interface Named { name: string }
interface Aged { age: number }
type Person = Named & Aged; // 常用方式
5.2 条件类型中的运用
在高级类型编程中,联合类型展现强大能力:
typescript复制type ExtractString<T> = T extends string ? T : never;
type Result = ExtractString<'a' | 1 | true>; // 得到 'a'
6. 真实案例:表单验证系统
最近用联合类型重构了一个表单验证器:
typescript复制type ValidationResult =
| { valid: true, value: string }
| { valid: false, error: string };
function validateEmail(email: string): ValidationResult {
return /^\S+@\S+$/.test(email)
? { valid: true, value: email }
: { valid: false, error: 'Invalid email' };
}
这种模式强制处理所有可能状态,消除了未处理错误分支的可能性。在团队协作中,新人也能快速理解必须处理哪些情况。