The in and instanceof Operators
Narrow object unions by asking which properties exist or which class an object came from.
When a union is made of object shapes rather than primitives, typeof is not enough β everything is just "object". Two operators pick up the slack.
The in operator
in checks whether a property exists, and TypeScript narrows to the members that have it:
type Dog = { bark: () => void };
type Cat = { meow: () => void };
function speak(pet: Dog | Cat) {
if ("bark" in pet) pet.bark(); // pet: Dog
else pet.meow(); // pet: Cat
}The instanceof operator
instanceof asks "was this built by that constructor?" β perfect for real classes and built-ins like Date, Error, or Map.
Reach for in with plain object shapes and interfaces; reach for instanceof when actual class instances are involved. Both are runtime checks, so unlike a type assertion they genuinely protect you at execution time.
Example
When to use it
- An event handler uses the in operator to check whether the event object has a key property before treating it as a keyboard event.
- An error boundary checks instanceof Error before accessing the message property on an unknown thrown value.
- A plugin system uses the in operator to detect which optional methods a plugin object provides before calling them.
More examples
in operator narrows object union
The in operator checks for the presence of a property and TypeScript narrows the union to whichever variant has that property.
type Circle = { radius: number };
type Square = { side: number };
function area(shape: Circle | Square): number {
if ('radius' in shape) {
return Math.PI * shape.radius ** 2; // shape is Circle
}
return shape.side ** 2; // shape is Square
}instanceof narrows class types
Chains instanceof from most specific to most general so each branch accesses the right properties.
class ApiError extends Error {
constructor(public status: number, message: string) {
super(message);
}
}
function handleError(err: unknown): string {
if (err instanceof ApiError) return `HTTP ${err.status}: ${err.message}`;
if (err instanceof Error) return err.message;
return 'Unknown error';
}Combining in and instanceof
Uses instanceof for a concrete class and the in operator for a structural duck-type check in the same function.
interface Flyable { fly(): void; }
class Bird implements Flyable {
fly(): void { console.log('flap'); }
sing(): void { console.log('tweet'); }
}
function act(obj: object): void {
if (obj instanceof Bird) {
obj.sing();
} else if ('fly' in obj) {
(obj as Flyable).fly();
}
}
Discussion