如何在 TypeScript 中检查类类型:从新手到专家
\n\n你是否曾经在 TypeScript 中编写代码,收到一个未知类型的对象,然后想:“你到底是什么?”?这个问题困扰着成千上万的开发者——难怪 TypeScript 中的类类型检查 这个话题在 Stack Overflow 上获得了数十万的浏览量。今天,我们将一劳永逸地弄清楚如何在 TypeScript 中正确检查类类型。
\n\n在本文中,你将了解:
\n- \n
- 为什么
typeof不适用于类 \n - 如何像使用瑞士军刀一样使用
instanceof\n - 什么是类型守卫以及它们如何拯救你的代码 \n
- 如何使用
is编写你自己的类型检查 \n - 来自实践的真实示例 \n
我们开始吧!
\n\n问题:为什么 typeof 在这里无能为力?
\n\n在 JavaScript 中,有一个 typeof 运算符,它对于原始类型非常有效:
typeof "hello" // "string"\ntypeof 42 // "number"\ntypeof true // "boolean"\n\n但是当涉及到类时,typeof 就失效了:
class Dog {}\nclass Cat {}\n\nconst pet = new Dog();\n\ntypeof pet; // "object" — 毫无用处!\ntypeof Dog; // "function" — 也不是我们想要的\n\n为什么?因为 JavaScript 中的类只是构造函数函数的语法糖。typeof 只能看到“对象”或“函数”,但无法区分它 具体是哪个 类。
这时,TypeScript 中的类类型检查 就登场了——这是一整套工具,可以帮助你精确地确定你正在处理的是哪个类的实例。
\n\n方法 1:instanceof —— 你的主要助手
\n\n检查类类型最直接、最清晰的方法是使用 instanceof 运算符。它检查该类是否存在于对象的原型链中:
class Animal {\n name: string;\n constructor(name: string) {\n this.name = name;\n }\n}\n\nclass Dog extends Animal {\n bark() {\n console.log("Woof!");\n }\n}\n\nclass Cat extends Animal {\n meow() {\n console.log("Meow!");\n }\n}\n\nfunction handleAnimal(animal: Animal) {\n if (animal instanceof Dog) {\n // TypeScript 知道 animal 是 Dog 类型\n animal.bark();\n } else if (animal instanceof Cat) {\n animal.meow();\n } else {\n console.log(`Unknown animal: ${animal.name}`);\n }\n}\n\nconst rex = new Dog("Rex");\nhandleAnimal(rex); // "Woof!"\n\n请注意:在检查 animal instanceof Dog 之后,TypeScript 会自动收窄类型(类型收窄),并允许你调用 Dog 的方法而不会出错。这就是 类型守卫 的魔力——内置的类型保护者。
什么时候 instanceof 不起作用?
\n- \n
- 处理不同的窗口/框架时(不同的全局对象) \n
- 如果类定义在不同模块且版本不同时 \n
- 检查接口时(接口在运行时不存在) \n
方法 2:使用 is 运算符创建自定义类型守卫
\n\n有时 instanceof 还不够。例如,你可能不仅想检查类,还想检查额外的条件。这时,使用 is 关键字的用户自定义类型守卫就派上用场了:
interface Bird {\n fly(): void;\n layEggs(): void;\n}\n\ninterface Fish {\n swim(): void;\n layEggs(): void;\n}\n\nfunction isFish(pet: Bird | Fish): pet is Fish {\n return (pet as Fish).swim !== undefined;\n}\n\nfunction move(pet: Bird | Fish) {\n if (isFish(pet)) {\n // TypeScript 知道:pet 是 Fish 类型\n pet.swim();\n } else {\n pet.fly();\n }\n}\n\n请注意返回类型中的语法:pet is Fish。这告诉 TypeScript:“如果这个函数返回 true,那么参数 pet 就是 Fish 类型”。现在,只要你使用