Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/typescript-exercises/typescript-exercises/llms.txt

Use this file to discover all available pages before exploring further.

Exercise 3: Type Narrowing with the in Operator

Scenario

You want to display additional information for each person - occupation for Users and role for Admins. However, TypeScript won’t let you access these properties directly on a union type.

The Problem

interface User {
    name: string;
    age: number;
    occupation: string;
}

interface Admin {
    name: string;
    age: number;
    role: string;
}

export type Person = User | Admin;

export function logPerson(person: Person) {
    let additionalInformation: string;
    if (person.role) {  // ❌ Error: Property 'role' doesn't exist on User
        additionalInformation = person.role;
    } else {
        additionalInformation = person.occupation;  // ❌ Error: Property 'occupation' doesn't exist on Admin
    }
    console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}
When working with union types, TypeScript only allows access to properties that exist on all members of the union. You must narrow the type first.

Key Concepts

Type Narrowing

The process of refining a union type to a more specific type

in Operator

Checks if a property exists on an object, narrowing the type accordingly

Understanding the in Operator

The in operator is a type guard that checks for property existence:
if ('role' in person) {
    // TypeScript knows person is Admin here
    person.role  // ✅ OK
} else {
    // TypeScript knows person is User here
    person.occupation  // ✅ OK
}

Solution

export function logPerson(person: Person) {
    let additionalInformation: string;
    if (person.role) {  // ❌ Error!
        additionalInformation = person.role;
    } else {
        additionalInformation = person.occupation;  // ❌ Error!
    }
    console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}

Why the Original Code Fails

The check if (person.role) tries to access the role property before checking if it exists. TypeScript doesn’t allow this on union types because:
  1. person could be a User, which doesn’t have role
  2. The property access itself would cause a compile error
The in operator checks for property existence without accessing it, allowing TypeScript to narrow the type safely.

How Type Narrowing Works

1

Start with Union

person: Person could be either User or Admin
2

Check Property

'role' in person checks if the property exists
3

Type Narrowed

Inside the if block, TypeScript narrows person to Admin
4

Else Branch

In the else block, TypeScript knows person must be User

Alternative Type Guards

While the in operator works well here, TypeScript offers other type narrowing techniques:
// For primitive types
if (typeof value === 'string') {
    value.toUpperCase()  // value is string
}
You’ll learn about type predicates in the next exercise, which provide even more control over type narrowing.

Common Pitfalls

Don’t use optional chaining for type narrowing:
if (person.role) { }  // ❌ Compile error
if (person?.role) { }  // ❌ Still an error - TypeScript checks types at compile time
if ('role' in person) { }  // ✅ Correct - proper type guard

Real-World Example

const persons: Person[] = [
    { name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
    { name: 'Jane Doe', age: 32, role: 'Administrator' },
    { name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
    { name: 'Bruce Willis', age: 64, role: 'World saver' }
];

persons.forEach(logPerson);
// Output:
//  - Max Mustermann, 25, Chimney sweep
//  - Jane Doe, 32, Administrator
//  - Kate Müller, 23, Astronaut
//  - Bruce Willis, 64, World saver

What You Learned

Use 'property' in object to check for property existence and narrow types
TypeScript tracks type narrowing through if/else branches automatically
Type guards prevent runtime errors by ensuring properties exist before access

Next Steps

Move on to Union Types to learn about discriminated unions and type predicates for even more powerful type narrowing.

Additional Resources