typeof 和 instanceof 在类型守卫中如何使用?
在 TypeScript 中,类型守卫 (Type Guards) 是一种通过条件语句检查变量类型,从而在特定代码块中“收窄”变量类型的机制。
typeof 和 instanceof 是 JavaScript 原生的操作符,TypeScript 能够识别它们并自动进行类型推断。
以下是它们的具体用法和区别:
1. typeof 类型守卫
typeof 主要用于检查基本数据类型(Primitives)。
适用范围
它可以识别以下返回值:
"string""number""boolean""symbol""undefined""object"(注意:null和数组也会返回这个,所以通常不用它来精确判断对象)"function""bigint"
如何使用
通常用于处理联合类型(Union Types),区分不同的基本类型。
typescript
function printId(id: number | string) {
// 在这个 if 语句之前,id 可能是 number 或 string
if (typeof id === "string") {
// 【类型守卫生效】
// TypeScript 知道在此块中,id 必定是 string 类型
console.log("ID is string: " + id.toUpperCase());
} else {
// TypeScript 自动推断在此块中,id 必定是 number 类型
console.log("ID is number: " + id.toFixed(2));
}
}
注意事项
null的陷阱:typeof null会返回"object"。如果你需要判断是否为对象且非 null,不能只用typeof。- 数组的陷阱:
typeof []也会返回"object"。
2. instanceof 类型守卫
instanceof 主要用于检查类(Class)的实例或构造函数创建的对象。它通过检查对象的原型链来判断。
适用范围
- 自定义的
class - 内置对象(如
Date,RegExp,Array,Error等)
如何使用
当你的类型是类的联合类型时,使用 instanceof 非常有效。
typescript
class Dog {
bark() { console.log("Woof!"); }
}
class Cat {
meow() { console.log("Meow!"); }
}
function interact(pet: Dog | Cat) {
// pet 可能是 Dog 或 Cat
if (pet instanceof Dog) {
// 【类型守卫生效】
// TypeScript 知道 pet 是 Dog 类型,可以调用 bark
pet.bark();
// pet.meow(); // 报错:Dog 类型上不存在 meow 方法
} else {
// TypeScript 推断 pet 是 Cat 类型
pet.meow();
}
}
用于内置对象
typescript
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString()); // x 被收窄为 Date
} else {
console.log(x.toUpperCase()); // x 被收窄为 string
}
}
3. 总结与对比
| 特性 | typeof |
instanceof |
|---|---|---|
| 检查对象 | 基本数据类型 (Primitives) | 类实例、对象 (Objects) |
| 判断依据 | 值的底层类型标签 | 原型链 (Prototype Chain) |
| 常见场景 | string vs number vs boolean |
ClassA vs ClassB,或 Date, Array |
| 局限性 | 无法区分普通对象、数组和 null (都返回 "object") |
无法判断基本类型 (如 123 instanceof Number 为 false) |
最佳实践建议
- 判断基本类型时:优先使用
typeof。- 例如:
if (typeof x === 'string')
- 例如:
- 判断类实例时:优先使用
instanceof。- 例如:
if (x instanceof MyClass)
- 例如:
- 判断数组时:不要用
typeof,也不建议用instanceof Array(虽然可行),推荐使用Array.isArray(),TypeScript 也能识别它作为类型守卫。 - 判断普通对象接口时:如果对象不是通过类创建的(只是普通的 JSON 对象),
typeof和instanceof都无法区分具体的接口形状(Interface)。此时需要使用 自定义类型守卫 (User-Defined Type Guards),即parameter is Type语法。
补充:自定义类型守卫示例(当 typeof/instanceof 不够用时)
typescript
interface Fish { swim: () => void; }
interface Bird { fly: () => void; }
// 自定义守卫:判断 pet 是否是 Fish
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // TS 知道这是 Fish
} else {
pet.fly(); // TS 知道这是 Bird
}
}