Skip to main content

Understanding `in` Operator Narrowing in TypeScript

ยท 4 min read

In TypeScript, the "in" operator narrowing is a type narrowing technique that allows the type checker to refine the types of union types based on the presence of a specific property or key in an object.

Understanding in Operator Narrowing ๐Ÿค”โ€‹

This feature is useful when you have a union of different object types, and you want to determine which specific type a variable belongs to at runtime.

TypeScript uses control flow analysis to narrow down the types of variables based on how they are used. This allows the type checker to provide better type safety and error detection.

Here's an example to demonstrate the concept of "in" operator narrowing:

type Shape = Circle | Square;

interface Circle {
kind: "circle";
radius: number;
}

interface Square {
kind: "square";
sideLength: number;
}

function getArea(shape: Shape): number {
if ("radius" in shape) {
// TypeScript knows that 'shape' must be of type Circle, as only Circle has the 'radius' property
return Math.PI * shape.radius ** 2;
} else {
// TypeScript knows that 'shape' must be of type Square, as it doesn't have the 'radius' property
return shape.sideLength ** 2;
}
}

In this example, we have two shape interfaces, Circle and Square, combined into a union type Shape. The getArea function accepts a shape of type Shape. Inside the function, we use the "in" operator to check if the "radius" property exists in the shape object. If it does, TypeScript narrows the type of shape to Circle, and we can safely access the radius property. If it doesn't, TypeScript narrows the type of shape to Square, and we can safely access the sideLength property.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

Time To Transition From JavaScript To TypeScript

Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.

SEE COURSE DETAILS

Real-World Example ๐ŸŒŽโ€‹

In a real-world TypeScript application, "in" operator narrowing can be particularly helpful when dealing with different types of events, responses, or actions that share some common properties but have different structures. Let's consider an example of a chat application that has multiple event types: message, file, and user status.

type ChatEvent = MessageEvent | FileEvent | UserStatusEvent;

interface MessageEvent {
eventType: "message";
senderId: number;
content: string;
timestamp: number;
}

interface FileEvent {
eventType: "file";
senderId: number;
fileName: string;
fileSize: number;
fileUrl: string;
timestamp: number;
}

interface UserStatusEvent {
eventType: "userStatus";
userId: number;
online: boolean;
lastActive: number;
}

function handleChatEvent(event: ChatEvent) {
if ("content" in event) {
// TypeScript narrows 'event' to type 'MessageEvent'
console.log(`Message from user ${event.senderId}: ${event.content}`);
} else if ("fileUrl" in event) {
// TypeScript narrows 'event' to type 'FileEvent'
console.log(`User ${event.senderId} sent a file: ${event.fileName}`);
} else if ("online" in event) {
// TypeScript narrows 'event' to type 'UserStatusEvent'
const status = event.online ? "online" : "offline";
console.log(`User ${event.userId} is now ${status}`);
}
}

const messageEvent: MessageEvent = {
eventType: "message",
senderId: 1,
content: "Hello!",
timestamp: Date.now(),
};

const fileEvent: FileEvent = {
eventType: "file",
senderId: 2,
fileName: "example.txt",
fileSize: 1024,
fileUrl: "https://example.com/files/example.txt",
timestamp: Date.now(),
};

const userStatusEvent: UserStatusEvent = {
eventType: "userStatus",
userId: 3,
online: false,
lastActive: Date.now() - 3600,
};

handleChatEvent(messageEvent);
handleChatEvent(fileEvent);
handleChatEvent(userStatusEvent);

In this example, we have three event types: MessageEvent, FileEvent, and UserStatusEvent, combined into a union type ChatEvent. The handleChatEvent function processes events of type ChatEvent. Inside the function, we use the " in" operator to narrow down the type of the event based on the presence of specific properties.

Using the "in" operator, TypeScript narrows down the type of the event, allowing us to safely access the properties specific to each event type. This ensures type safety and prevents errors while handling different types of events in our chat application.

What Can You Do Next ๐Ÿ™๐Ÿ˜Šโ€‹

If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for ssoftware developers.

YouTube @cloudaffle