liveBook · Manning

Type guard

As explained in Essential TypeScript 5, Third Edition

A type guard is a runtime-checkable condition that allows the TypeScript compiler to narrow an expression to a more specific type within a code path.

A type guard checks a value at runtime so TypeScript can treat it as a more specific type in the code path where the check succeeds. Use one when a union permits several types but an operation belongs to only one of them. Unlike a type assertion, a guard tests the value before narrowing it. section 7.5 section 7.8

Narrow a tax result

In src/index.ts, a tax calculation returns string | number. The number-specific call is safe inside its guarded branch: `if (typeof taxValue === "number") { console.log(taxValue.toFixed(2)); }`. A `typeof taxValue === "string"` branch can instead call `charAt(0)`. A `switch (typeof taxValue)` narrows the value in its corresponding cases. section 7.5

Choose a check that distinguishes the possible values:

  • For primitive values, use JavaScript’s `typeof`. It cannot distinguish two object shapes: both produce `"object"`. section 7.5 section 10.2.6
  • For object unions, test a property unique to one shape, such as `"city" in item` for an object that may be a Person or a Product. Testing a property shared by both shapes cannot distinguish them. section 10.2.6
  • For nullable values, check the value explicitly. JavaScript reports `typeof null` as `"object"`; `if (taxValue !== null)` removes null from a string | number | null union inside that block. section 7.7.1 section 7.7.3
Listing 10.15 Type guarding in the index.ts file in the src folder
type Product = {
    id: number,
    name: string, 
    price?: number
};

type Person = {
    id: string,
    name: string, 
    city: string
};

let hat = { id: 1, name: "Hat", price: 100 };
let gloves = { id: 2, name: "Gloves", price: 75 };
let umbrella = { id: 3, name: "Umbrella", price: 30 };
let bob = { id: "bsmith", name: "Bob", city: "London" };

let dataItems: (Product | Person)[] = [hat, gloves, umbrella, bob];

dataItems.forEach(item => {
    if ("city" in item) {
        console.log(`Person: ${item.name}: ${item.city}`);                
    } else  {        
        console.log(`Product: ${item.name}: ${item.price}`);
    }
});

The src/index.ts listing checks for `city` while processing a (Product | Person)[] array, then accesses properties appropriate to each branch. section 10.2.6

A reusable guard can return a type predicate, such as `function isPerson(testObj: any): testObj is Person { return testObj.city !== undefined; }`. A successful `isPerson(item)` check lets the guarded branch use Person properties. The same approach enables type-safe access to a method added by a class decorator, even though the decorator does not change the class’s declared type. section 10.2.6 section 14.3.1

Guards also help check whether every member of a union has been handled: after number and string cases exhaust a string | number result, an unexpected value in the default case has type `never`. Keep nullable possibilities explicit with `strictNullChecks`; otherwise a function may return null without its declared union revealing that possibility. For objects created by the demonstrated constructor function, use a property check rather than relying on `instanceof` as a TypeScript guard. section 7.5.1 section 11.2

How Essential TypeScript 5, Third Edition covers it

How deep it goes
Works through primitive, nullable, and object checks, reusable predicates, and exhaustive narrowing; does not detail compiler internals.
Examples
Uses tax results, tuple elements, object arrays, and a decorator-added method to demonstrate checks in application code.
What it assumes
Assumes familiarity with JavaScript conditions and operators while explaining how TypeScript recognizes them as guards.
What you can do after
Apply runtime checks and predicate functions to narrow unions, handle null, and access type-specific properties or methods.
How it connects
Connects type guards to unions, type assertions, strict null checks, object shapes, type predicates, and never.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Essential TypeScript 5, Third Edition. The section links point to the parts of the book it draws on.