TypeScript 的类型保护(Type Guards)是一种机制,用于在代码中根据特定条件细化变量的类型,从而确保代码在运行时更安全和准确。类型保护可以通过几种不同的方式实现,包括类型断言、typeof 检查、instanceof 检查和用户定义的类型保护函数。
typeof 检查
typeof 检查主要用于基本类型(如 string、number、boolean、symbol)的类型保护。
function padLeft(value: string, padding: string | number) {
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value;
}
if (typeof padding === 'string') {
return padding + value;
}
throw new Error(`Expected string or number, got '${typeof padding}'.`);
}
在上面的这些代码中,typeof padding === "number"
和 typeof padding === "string"
分别对 padding 进行类型保护,确保在每个条件分支中 padding 的类型是确定的。
instanceof 检查
instanceof 检查用于对象类型之间的类型保护。它通常用于检查一个对象是否是某个类的实例。
class Bird {
fly() {
console.log('Flying');
}
}
class Fish {
swim() {
console.log('Swimming');
}
}
type Pet = Bird | Fish;
function getPetAction(pet: Pet) {
if (pet instanceof Bird) {
pet.fly();
} else if (pet instanceof Fish) {
pet.swim();
}
}
const bird = new Bird();
const fish = new Fish();
getPetAction(bird); // Flying
getPetAction(fish); // Swimming
在上面的这些代码中,pet instanceof Bird 和 pet instanceof Fish 分别对 pet 进行类型保护,确保在每个条件分支中 pet 的类型是确定的。
自定义类型保护
自定义类型保护是通过定义一个返回类型谓词(type predicate)的函数实现的。类型谓词的格式是 parameterName is Type,表示如果函数返回 true,则该参数属于指定的类型。
interface Bird {
kind: 'bird';
fly(): void;
}
interface Fish {
kind: 'fish';
swim(): void;
}
type Pet = Bird | Fish;
function isBird(pet: Pet): pet is Bird {
return pet.kind === 'bird';
}
function getPetAction(pet: Pet) {
if (isBird(pet)) {
pet.fly();
} else {
pet.swim();
}
}
const bird: Bird = { kind: 'bird', fly: () => console.log('Flying') };
const fish: Fish = { kind: 'fish', swim: () => console.log('Swimming') };
getPetAction(bird); // Flying
getPetAction(fish); // Swimming
在上面的这些代码中,isBird 函数通过检查 pet.kind 是否为 “bird” 来进行类型保护,确保在 if (isBird(pet)) 分支中,pet 的类型为 Bird。
联合类型和类型保护
联合类型(Union Types)可以结合类型保护来使用,以确保在不同的分支中变量的类型是确定的。
type StringOrNumber = string | number;
function processValue(value: StringOrNumber) {
if (typeof value === 'string') {
console.log('String value:', value.toUpperCase());
} else if (typeof value === 'number') {
console.log('Number value:', value.toFixed(2));
} else {
console.log('Unexpected type');
}
}
processValue('hello'); // String value: HELLO
processValue(123.456); // Number value: 123.46
在上面的这些代码中,typeof value === “string” 和 typeof value === “number” 分别对 value 进行类型保护,确保在每个条件分支中 value 的类型是确定的。
in 关键字实现类型保护
在 TypeScript 中,in 关键字可以用于实现类型保护(Type Guards),帮助你在类型不明确时进行类型检查,并保证特定类型的属性在安全范围内使用。in 关键字可以检查对象中是否存在某个属性,从而进行类型保护。
interface Cat {
meow: () => void;
}
interface Dog {
bark: () => void;
}
type Pet = Cat | Dog;
你想要编写一个函数,该函数接受一个 Pet 类型的参数,并根据具体类型调用相应的方法。你可以使用 in 关键字来实现类型保护:
function makeSound(pet: Pet) {
if ('meow' in pet) {
pet.meow(); // TypeScript 知道这里是 Cat 类型
} else {
pet.bark(); // TypeScript 知道这里是 Dog 类型
}
}
在这个例子中,in 关键字检查对象 pet 中是否存在 meow 属性。如果存在,TypeScript 会将 pet 视为 Cat 类型,并允许你调用 meow 方法。否则,TypeScript 会将 pet 视为 Dog 类型,并允许你调用 bark 方法。
总结
TypeScript 的类型保护通过不同的机制(如 typeof 检查、instanceof 检查、in 检查、自定义类型保护函数等)提供了一种在代码运行时细化变量类型的方法,从而提高了代码的类型安全性和可维护性。这些机制使得开发者可以在编写代码时充分利用 TypeScript 的类型系统,避免类型错误,并使代码更加健壮。