什么是类型保护与类型守卫
在TypeScript中,类型保护(Type Guards)和类型守卫是帮助我们在代码中缩小变量类型范围的机制。它们允许TypeScript编译器根据某些条件判断来推断出更具体的类型,从而在特定代码块中提供更精确的类型检查。
为什么需要类型保护
JavaScript是动态类型语言,而TypeScript为其添加了静态类型系统。但在实际开发中,我们经常需要处理可能是多种类型的变量。例如:
typescript
function printLength(value: string | number) {
// 这里value可能是string或number
console.log(value.length); // 错误:number类型没有length属性
}
这时就需要类型保护来告诉TypeScript在特定条件下变量的确切类型。
常见的类型保护方式
1. typeof类型保护
使用typeof
操作符可以检查基本类型:
typescript
function printLength(value: string | number) {
if (typeof value === 'string') {
console.log(value.length); // 这里value被推断为string
} else {
console.log(value.toFixed(2)); // 这里value被推断为number
}
}
2. instanceof类型保护
对于类实例,可以使用instanceof
:
typescript
class Bird {
fly() {
console.log('Flying');
}
}
class Fish {
swim() {
console.log('Swimming');
}
}
function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly(); // 这里animal被推断为Bird
} else {
animal.swim(); // 这里animal被推断为Fish
}
}
3. in操作符类型保护
检查对象是否具有特定属性:
typescript
interface Dog {
bark(): void;
}
interface Cat {
meow(): void;
}
function makeSound(pet: Dog | Cat) {
if ('bark' in pet) {
pet.bark(); // 这里pet被推断为Dog
} else {
pet.meow(); // 这里pet被推断为Cat
}
}
4. 自定义类型保护函数
可以创建返回类型谓词的自定义函数:
typescript
function isString(value: any): value is string {
return typeof value === 'string';
}
function example(value: string | number) {
if (isString(value)) {
console.log(value.length); // value被推断为string
} else {
console.log(value.toFixed(2)); // value被推断为number
}
}
类型守卫的高级用法
可辨识联合(Discriminated Unions)
当联合类型中的每个成员都有一个共同的属性(通常是字面量类型)时,可以使用可辨识联合:
typescript
interface Square {
kind: 'square';
size: number;
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number;
}
type Shape = Square | Rectangle;
function area(shape: Shape) {
switch (shape.kind) {
case 'square':
return shape.size * shape.size; // shape被推断为Square
case 'rectangle':
return shape.width * shape.height; // shape被推断为Rectangle
}
}
非空断言
使用!
操作符可以告诉TypeScript某个值不为null或undefined:
typescript
function fixed(name: string | null) {
console.log(name!.toUpperCase()); // 非空断言
}
最佳实践
- 优先使用内置的类型保护(typeof, instanceof, in)
- 对于复杂类型,创建自定义类型保护函数
- 使用可辨识联合模式处理复杂的联合类型
- 谨慎使用非空断言,确保逻辑上确实不会为null/undefined
类型保护与类型守卫是TypeScript强大类型系统的关键特性之一,合理使用可以显著提高代码的类型安全性和开发体验。