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
