一、类型守卫是什么?
在TypeScript的世界里,类型守卫就像是一个智能门卫,它能帮你在代码运行时判断一个变量到底属于哪种具体类型。想象一下,你有一个可能是字符串也可能是数字的变量,而类型守卫就是那个能帮你准确分辨它真实身份的工具。
举个例子:
function printValue(value: string | number) {
if (typeof value === 'string') {
// 这里value被确定为string类型
console.log(value.toUpperCase()); // 可以安全调用字符串方法
} else {
// 这里value被确定为number类型
console.log(value.toFixed(2)); // 可以安全调用数字方法
}
}
这个简单的typeof检查就是最基础的类型守卫。它会根据运行时检查结果,自动缩小变量的类型范围,让你的代码更安全。
二、常见的类型守卫方式
1. typeof守卫
最直接的方式,适合处理基本类型(string、number等):
function handleValue(val: string | number) {
if (typeof val === 'string') {
return val.trim(); // 明确是字符串
}
return val.toFixed(); // 明确是数字
}
2. instanceof守卫
处理自定义类或内置对象时特别有用:
class Dog { bark() { console.log('汪!') } }
class Cat { meow() { console.log('喵~') } }
function play(pet: Dog | Cat) {
if (pet instanceof Dog) {
pet.bark(); // 类型确定为Dog
} else {
pet.meow(); // 类型确定为Cat
}
}
3. 自定义类型谓词
当内置方法不够用时,可以自己定义类型判断函数:
interface Fish { swim(): void }
interface Bird { fly(): void }
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // 类型确定为Fish
} else {
pet.fly(); // 类型确定为Bird
}
}
注意pet is Fish这个返回值类型声明,它告诉TypeScript这个函数是个类型守卫。
三、进阶应用场景
1. 处理联合类型
当API返回复杂数据时特别实用:
type APIResponse =
| { status: 'success'; data: string[] }
| { status: 'error'; message: string };
function handleResponse(response: APIResponse) {
if (response.status === 'success') {
console.log(response.data.join(', ')); // 确定是成功响应
} else {
console.error(response.message); // 确定是错误响应
}
}
2. 配合数组过滤使用
const mixedArray = [1, 'hello', 2, 'world', true];
// 过滤出所有字符串
const stringsOnly = mixedArray.filter((item): item is string => {
return typeof item === 'string';
});
stringsOnly.forEach(str => {
console.log(str.toUpperCase()); // 安全操作,因为类型确定为string[]
});
四、技术细节与注意事项
性能考量:简单的typeof检查几乎没有性能开销,但复杂的自定义守卫可能会影响性能,特别是在热路径代码中。
过度使用警告:不要为了类型安全而把代码变成全是类型检查的"防御性编程",找到平衡点很重要。
与any类型的交互:类型守卫是逃离any类型的重要方式之一:
function processValue(val: any) {
if (typeof val === 'string') {
// 现在val从any缩小为string
console.log(val.length);
}
}
- 浏览器兼容性:某些特殊场景下(如区分null和object),不同浏览器的typeof行为可能不一致,需要注意。
五、最佳实践总结
- 优先使用简单的typeof和instanceof守卫
- 复杂逻辑封装成自定义类型谓词
- 联合类型配合判别属性(discriminant)是绝配
- 数组过滤时显式声明类型谓词
- 避免在类型守卫中做有副作用的操作
类型守卫是TypeScript类型系统的精髓之一,它让静态类型检查有了动态的灵活性。掌握好这个特性,能让你的代码既安全又优雅,就像给你的程序装上了智能导航,既不会迷路又能选择最优路径。
评论