How to Check a Class Type in TypeScript: From Beginner to Pro
\n\nHave you ever written code in TypeScript, received an object of an unknown type, and thought: "Who are you, really?"? This question plagues thousands of developers — it's no wonder the topic of class type check in TypeScript has garnered hundreds of thousands of views on Stack Overflow. And today, we will finally figure out how to properly check class types in TypeScript.
\n\nIn this article, you will learn:
\n- \n
- Why
typeofdoesn't work for classes \n - How to use
instanceoflike a Swiss Army knife \n - What type guards are and how they save your code \n
- How to write your own type check using
is\n - Real-world practical examples \n
Let's go!
\n\nThe Problem: Why is typeof Powerless Here?
\n\nIn JavaScript, there is the typeof operator, which works perfectly with primitives:
typeof "hello" // "string"\ntypeof 42 // "number"\ntypeof true // "boolean"\n\nBut when it comes to classes, typeof falls short:
class Dog {}\nclass Cat {}\n\nconst pet = new Dog();\n\ntypeof pet; // "object" — useless!\ntypeof Dog; // "function" — not what we need either\n\nWhy? Because classes in JavaScript are just syntactic sugar over constructor functions. typeof only sees "object" or "function" but doesn't distinguish which specific class it is.
This is where class type check in TypeScript comes into play — a whole arsenal of tools that will help you accurately determine which class instance you are dealing with.
\n\nMethod 1: instanceof — Your Main Helper
\n\nThe most direct and understandable way to check a class type is the instanceof operator. It checks whether the class exists in the object's prototype chain:
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 knows that animal is a 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\nNotice: after the animal instanceof Dog check, TypeScript automatically narrows the type (type narrowing) and allows you to call Dog methods without errors. This is the magic of type guards — built-in type protectors.
When does instanceof not work?
\n- \n
- When working with different windows/frames (different global objects) \n
- If classes are defined in different modules with different versions \n
- When checking interfaces (they don't exist at runtime) \n
Method 2: Custom Type Guards with the is Operator
\n\nSometimes instanceof is not enough. For example, you might want to check not only the class but also additional conditions. This is where custom type guards with the is keyword come to the rescue:
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 knows: pet is a Fish\n pet.swim();\n } else {\n pet.fly();\n }\n}\n\nNotice the syntax: pet is Fish in the return type. This tells TypeScript: "If this function returns true, then the parameter pet is a Fish." Now TypeScript will narrow the type everywhere you use