在 TypeScript 中,我们常常需要判断一个对象属于某种类型。以下是几种常见的类型判断方式:
1. instanceof
操作符
原理:
判断对象是否是某个构造函数(类)创建的实例。
ts
class Dog {
bark() {}
}
const d = new Dog();
console.log(d instanceof Dog); // true
优点:
- 简洁直观。
- 适用于类(
class
)实例判断。
缺点:
- 只能用于类,不适用于接口(
interface
)。 - 无法跨 iframe 或跨 window 使用。
- 在前端库中用得较少,因为很多类型是接口而不是类。
2. 判别联合类型(Discriminated Union)
原理:
使用一个 字面量类型属性(通常叫 kind/type/tag) 来区分联合类型。
ts
type Circle = { kind: 'circle'; radius: number };
type Square = { kind: 'square'; side: number };
type Shape = Circle | Square;
function getArea(shape: Shape): number {
if (shape.kind === 'circle') {
return Math.PI * shape.radius ** 2;
} else {
return shape.side ** 2;
}
}
优点:
- 简洁、类型安全。
- IDE 自动提示好。
- 编译器能进行详尽的类型缩小和检查(如
switch
的穷尽检查)。
缺点:
- 依赖于人为维护
kind
字段,一旦忘记添加或拼写错误,就会出错。 - 不适用于运行时动态类型的判断。
3. 自定义类型保护函数(Type Predicate)
原理:
编写返回布尔值的函数,配合返回值类型谓词来帮助 TypeScript 缩小类型。
ts
type Cat = { meow: () => void };
type Dog = { bark: () => void };
function isCat(pet: Cat | Dog): pet is Cat {
return (pet as Cat).meow !== undefined;
}
优点:
- 类型缩小灵活,支持复杂逻辑判断。
- 可复用、单元测试友好。
缺点:
- 需要手动书写,较繁琐。
- 如果判断逻辑不准确可能造成错误类型推断。
4. typeof
操作符
原理:
判断原始类型(number, string, boolean, symbol, bigint, undefined, function)。
ts
function doSomething(x: number | string) {
if (typeof x === "string") {
console.log(x.toUpperCase());
}
}
优点:
- 语法简单。
- 编译器原生支持。
缺点:
- 只能用于原始类型,无法用于对象类型或自定义类型。
5. 属性存在性检查(in 操作符)
原理:
通过判断某属性是否在对象中来缩小类型。
ts
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
animal.swim();
} else {
animal.fly();
}
}
优点:
- 简单,适合对象类型判断。
- 无需定义额外的字段。
缺点:
- 属性名可能冲突或变动,带来维护成本。
- 不能完全替代类型标识字段。
6. 结构判断(Duck Typing)
原理:
依赖对象结构自动推断类型。
ts
function greet(obj: { name: string }) {
console.log(obj.name);
}
优点:
- 灵活,无需显式定义类型。
缺点:
- 容易造成类型混淆。
- 难以进行运行时安全判断。
比较总结
方法 | 可判断范围 | 运行时可用 | 类型安全 | 简洁性 | 适用场景 |
---|---|---|---|---|---|
instanceof |
类实例 | ✅ | ✅ | ✅ | 判断类(class)实例 |
判别联合类型(Discriminated Union) | 联合类型(需标记字段) | ❌ | ✅ | ✅✅ | 类型定义中区分类别 |
类型保护函数 | 任意类型 | ✅ | ✅✅ | ❌ | 通用复杂类型判断 |
typeof |
原始类型 | ✅ | ✅ | ✅ | 字符串、数字、布尔等基础类型 |
in 操作符 |
对象结构判断 | ✅ | ✅ | ✅ | 判断某字段是否存在 |
结构判断(Duck Typing) | 任意结构 | ✅ | ❌ | ✅ | 临时快速判断,不推荐严谨使用 |
最佳实践建议
-
首选:判别联合类型
- 用于类型系统设计中的联合类型判断。
- 优雅且类型安全,支持 IDE 推断。
-
类对象:使用
instanceof
- 判断是否为某个类的实例。
-
复杂判断逻辑:自定义类型保护函数
- 用于多字段综合判断。
- 建议封装成独立函数,便于测试和复用。
-
原始类型:使用
typeof
- 简单有效。
-
对象字段存在性判断:使用
in
- 适合有明显结构区分的类型。
示例:综合使用示意
ts
type Cat = { kind: 'cat'; meow: () => void };
type Dog = { kind: 'dog'; bark: () => void };
type Animal = Cat | Dog;
function handleAnimal(animal: Animal) {
switch (animal.kind) {
case 'cat':
animal.meow();
break;
case 'dog':
animal.bark();
break;
}
}