Suggest an editImprove this articleRefine the answer for “Code safety and narrowing”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)Narrowing improves code safety because TypeScript guarantees that in a particular branch a variable genuinely has a specific type, so accessing its properties or methods is safe. **Key point:** thanks to narrowing, errors such as accessing a non-existent property or calling a method on `null`/`undefined` are caught at compile time rather than at runtime.Shown above the full answer for quick recall.Answer (EN)Image## 1. Prevents access to non-existent properties Without narrowing, TypeScript cannot know which fields are available and might allow an unsafe access. ```javascript function printId(id: string | number) { console.log(id.toUpperCase()); // Error: number has no toUpperCase } ``` With narrowing: ```javascript function printId(id: string | number) { if (typeof id === "string") { console.log(id.toUpperCase()); // safe } else { console.log(id.toFixed(2)); // safe } } ``` **Why it's safe:** TypeScript **guarantees** that in the first branch `id` is definitely `string`, and in the second it's `number`. Incompatibility errors are caught **at compile time**, not at runtime. --- ## 2. Rules out "impossible" execution paths Type narrowing lets the compiler analyze control flow and *remove unreachable code branches*. ```javascript function process(value: string | null) { if (value === null) return; // Here value is guaranteed to be string console.log(value.toUpperCase()); // safe } ``` Without the `if (value === null)` check, calling `toUpperCase()` could lead to a **runtime error** (`Cannot read properties of null`). --- ## 3. Makes code self-documenting and obvious Narrowing is not only about protection, it's also about **readability**: ```javascript type Response = | { status: "success"; data: string } | { status: "error"; message: string }; function handle(res: Response) { if (res.status === "success") { console.log(res.data); // safe } else { console.log(res.message); // safe } } ``` TypeScript uses the `status` discriminant, understands which variant is active, and suggests only the allowed properties in the IDE. **Benefit:** no "accidental" access to `res.data` in the wrong branch. The IDE simply won't let you do it. --- ## 4. Reduces the likelihood of `undefined` and `null` errors Among the most common runtime errors in JS: > `Cannot read property 'x' of undefined` Type narrowing helps rule out such situations **before the code even runs**: ```javascript function showUser(user?: { name: string } | null) { if (!user) return; // cut off undefined and null console.log(user.name); // definitely exists } ``` TS "sees" that after `if (!user) return` the variable `user` **cannot be** `undefined | null`. So access to `user.name` is safe. --- ## 5. Increases safety when working with union types Without narrowing, TypeScript doesn't know which union member is being used. ```javascript type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number }; function area(shape: Shape) { if (shape.kind === "circle") { return Math.PI * shape.radius ** 2; // safe } else { return shape.side ** 2; // safe } } ``` If TS did not narrow the type by `kind`, calling `shape.radius` could access a field that `square` doesn't have, leading to a runtime error. --- ## 6. Helps with logical branching and exceptions TypeScript knows how to understand which types are **excluded** after `throw`, `return`, `break`, and so on. ```javascript function getLength(value: string | null) { if (value === null) throw new Error("Empty value"); // Here value is definitely string return value.length; // safe } ``` Thanks to narrowing, after `throw` TypeScript knows: `value` is not `null`. --- ## 7. Helps the IDE suggest the correct methods and properties When a type is narrowed, the IDE suggests only the methods that actually exist on the specific type: ```javascript function log(x: string | Date) { if (x instanceof Date) { x.toISOString(); // suggestion only for Date } else { x.toUpperCase(); // suggestion only for string } } ``` This **reduces the risk of accidentally calling a non-existent method**. Types and autocomplete become context-aware. --- ## 8. Enables exhaustiveness checks Narrowing lets you guarantee that you have **handled all possible variants of a type**. ```javascript type Status = "loading" | "success" | "error"; function handle(status: Status) { switch (status) { case "loading": break; case "success": break; case "error": break; default: const _exhaustive: never = status; // Error if a new status appears } } ``` TypeScript guarantees that no variant is missed. This is **absolute protection against logical errors**. --- ## 9. Helps work safely with `unknown` The `unknown` type requires **narrowing** before it can be used. This protects against "blind" mistakes: ```javascript function process(value: unknown) { if (typeof value === "string") { console.log(value.toUpperCase()); // safe } else { // console.log(value.toUpperCase()); // error - not a string } } ``` TypeScript won't let you use `value` until you've proven it is a safe type. --- ## 10. Protects against incorrect casts (`as`) Without narrowing, you have to "forcibly" cast types: ```javascript (user as { name: string }).name; // risky - TS just takes your word for it ``` With narrowing: ```javascript if (typeof user === "object" && user && "name" in user) { console.log(user.name); // safe, proven by the logic } ``` TypeScript doesn't "take your word for it", it itself **proves correctness** through conditions. --- ## Summary | What narrowing provides | Why it's safe | | --- | --- | | Rules out impossible types | TS won't let you call methods on a non-existent type | | Guarantees correct properties | Object access only after a check | | Prevents `null` / `undefined` errors | The compiler knows the value is no longer "empty" | | Simplifies checks | The IDE knows the context and suggests the right methods | | Ensures exhaustive handling of a union type | No variant can be forgotten | | Minimizes runtime errors | Errors are caught at compile time |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.