Type Narrowing
Using in with Type Guards

As we write more types, we’re bound to create custom types to better describe our data’s properties and methods. While using typeof can get us pretty far, sometimes we want to see if a specific method exists on a type instead of a type like 'string'. That’s where the in operator comes into play. The in operator checks if a property exists on an object itself or anywhere within its prototype chain. Take a look at this example:

type Tennis = { serve: () => void; } type Soccer = { kick: () => void; } function play(sport: Tennis | Soccer) { if ('serve' in sport) { return sport.serve(); } if ('kick' in sport) { return sport.kick(); } }

In the example above, we check if the 'serve' property exists on sport with the in operator. The 'serve' property must exist inside the conditional, so TypeScript can narrow sport‘s type inside the conditional to be of type Tennis. This type narrowing is possible because TypeScript recognizes in as a type guard.



In the code editor, there are two types: Cat and Fish. Underneath the types are variables for each: siameseCat and bettaFish. Finally, there’s a move() function that takes a pet as its argument. Ultimately, we want the move() function to call the correct movement method on pet.

Currently our code has a TypeScript error because we’re calling .run() on pet inside the move() function. Let’s fix it.

Start by writing a type guard using in inside the move() function that checks if 'run' exists on pet. If it does, then return the value of pet.run().


The previous checkpoint takes care of Cat types. Now let’s handle Fish types.

Write another type guard using the in operator inside the body of the move() function. This type guard should check if the property 'swim' exists on pet. If it does, then return pet.swim().


Run tsc in the terminal to compile your code. There should be no errors, even though methods we called on pet do not exist on both Cat and Fish types, since TypeScript was able to type narrow.


Run your compiled code with node index.js. You should see pitter pat as the output if you called move() with simaeseCat or bubble blub if you called move() with bettaFish.

Folder Icon

Take this course for free

Already have an account?