类型保护与区分类型
在 TypeScript 中,类型保护是一种用于在运行时确定变量类型的机制,以便在特定的代码分支中进行更精确的类型操作。在处理联合类型或类型可能发生变化的情况下,确保代码的类型安全性和准确性。
从概念上讲,"区分类型"通常指的是在编程中明确地识别和处理不同的类型。
区分类型常常通过类型保护来实现。目的是为了在处理联合类型或可能具有多种类型的变量时,能够根据其实际的类型进行不同的操作。
typeof 类型保护
用于基本类型(如 number
、string
、boolean
、symbol
等)的判断。
typescript
function processValue(value: number | string) {
if (typeof value === "number") {
// 这里 value 被确定为 number 类型
console.log(value + 1);
} else {
// 这里 value 被确定为 string 类型
console.log(value + "1");
}
}
processValue(1); // 2
processValue("1"); // "11"
typeof类型保护 只有两种形式能被识别: typeof v === "typename"
和 typeof v !== "typename"
, typename
必须是 number
, string
, boolean
或 symbol
。
instanceof 类型保护
instanceof
类型保护是通过构造函数来细化类型的一种方式。
用于类实例的类型判断:运行时确定一个对象是否是某个类的实例。
示例:
typescript
class Animal {
move() {
console.log("The animal is moving.");
}
}
class Dog extends Animal {
run() {
console.log("The dog is running!");
}
}
class Cat extends Animal {
}
function doSomethingWithAnimal(animal: Animal) {
if (animal instanceof Dog) {
// 在这里,TypeScript 知道 animal 是 Dog 类型
animal.run();
} else {
animal.move();
}
}
let myDog = new Dog();
doSomethingWithAnimal(myDog); // The dog is running!
let myCat = new Cat();
doSomethingWithAnimal(myCat); // "The animal is moving."
在上述示例中,当 animal
被判断为 instanceof Dog
时,TypeScript 会将 animal
的类型细化为 Dog
类型,从而允许访问 Dog
类特有的方法。
instanceof
的右侧要求是一个构造函数,TypeScript将细化为:
- 如果此构造函数的
prototype
属性的类型不是any
类型,那么 TypeScript 会将其细化为该prototype
属性的类型。 - 如果构造函数具有构造签名(即定义了如何创建实例的方式),那么会将构造签名所返回的类型也纳入考虑,形成一个联合类型。
instanceof
类型保护的原理是基于 JavaScript 中对象的原型链机制。只有当对象的原型链中包含了指定类的原型对象时,instanceof
才会返回 true
。
注意: instanceof
只适用于类创建的对象,对于普通的对象或者原始类型是不适用的。
in 操作符类型保护
用于检查对象是否具有特定的属性。
示例:
typescript
interface Bird {
fly(): void;
}
interface Fish {
swim(): void;
}
function handleCreature(creature: Bird | Fish) {
if ('fly' in creature) {
// 这里 creature 被确定为 Bird 类型
creature.fly();
} else {
// 这里 creature 被确定为 Fish 类型
creature.swim();
}
}
function getSmallPet1(): Fish | Bird {
return {
fly: () => console.log("fly")
}
}
function getSmallPet2(): Fish | Bird {
return {
swim: () => console.log("swim")
}
}
let pet1 = getSmallPet1();
handleCreature(pet1); // fly
let pet2 = getSmallPet2();
handleCreature(pet2); // swim
用户自定义的类型保护
用户自定义的类型保护函数是通过返回一个类型谓词来实现的。类型谓词的形式是 parameterName is Type
,其中 parameterName
是要检查的参数名称, Type
是要断言的类型。
把上面的示例改成自定义的类型保护:
typescript
function isBird(creature: Bird | Fish): creature is Bird {
return (creature as Bird).fly!== undefined;
}
function handleCreature(creature: Bird | Fish) {
if (isBird(creature)) {
creature.fly();
} else {
creature.swim();
}
}
isBird
函数就是一个用户自定义的类型保护函数。它根据对象是否具有 fly
方法来判断传入的对象是否为 Bird
类型。
creature is Bird
就是类型谓词。
每当使用一些变量调用 isBird
时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。