The in operator checks whether an object has a property and narrows the union.

property guard `"address" in value` narrows a union to the object shape that owns that property.

In Operator Guard

channel
in_guard.ts
Replay: real traced execution (multi-file project)
type EmailContact = {
    address: string;
};

type SmsContact = {
    phone: string;
};

function routeContact(contact: EmailContact | SmsContact): string {
    if ("address" in contact) {
        return `email:${contact.address}`;
    }
    return `sms:${contact.phone}`;
}

const channel: string = "email";
const contact: EmailContact | SmsContact =
    channel === "email"
        ? { address: "ada@example.com" }
        : { phone: "555-0100" };

console.log(routeContact(contact));
type EmailContact = {
    address: string;
};

type SmsContact = {
    phone: string;
};

function routeContact(contact: EmailContact | SmsContact): string {
    if ("address" in contact) {
        return `email:${contact.address}`;
    }
    return `sms:${contact.phone}`;
}

const channel: string = "sms";
const contact: EmailContact | SmsContact =
    channel === "email"
        ? { address: "ada@example.com" }
        : { phone: "555-0100" };

console.log(routeContact(contact));
  1. channel ← email, contact ← [object Object]

    13    return `sms:${contact.phone}`;14}1516const channel→ email: string = "email";  //@channel="sms"17const contact→ [object Object]: EmailContact | SmsContact =18    channel === "email"19        ? { address: "ada@example.com" }20        : { phone: "555-0100" };2122console.log(routeContact(contact[object Object]));
  2. function routeContact(contact: EmailContact | SmsContact): string

    6    phone: string;7};89function routeContact(contact[object Object]: EmailContact | SmsContact): string {10    if ("address" in contact) {
  3. if ("address" in contact)

    9function routeContact(contact: EmailContact | SmsContact): string {10    if ("address" in contact[object Object]) {11        return `email:${contact.addressada@example.com}`;12    }
  4. console.log(routeContact(contact));

    19        ? { address: "ada@example.com" }20        : { phone: "555-0100" };2122console.log(routeContact(contact[object Object]));
    outputemail:ada@example.com
  1. channel ← sms, contact ← [object Object]

    13    return `sms:${contact.phone}`;14}1516const channel→ sms: string = "sms";17const contact→ [object Object]: EmailContact | SmsContact =18    channel === "email"19        ? { address: "ada@example.com" }20        : { phone: "555-0100" };2122console.log(routeContact(contact[object Object]));
  2. function routeContact(contact: EmailContact | SmsContact): string

    6    phone: string;7};89function routeContact(contact[object Object]: EmailContact | SmsContact): string {10    if ("address" in contact) {11        return `email:${contact.address}`;12    }13    return `sms:${contact.phone555-0100}`;14}
  3. console.log(routeContact(contact));

    19        ? { address: "ada@example.com" }20        : { phone: "555-0100" };2122console.log(routeContact(contact[object Object]));
    outputsms:555-0100