Suggest an editImprove this articleRefine the answer for “What is type narrowing in TypeScript?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Type narrowing** is the process by which TypeScript refines a variable's type based on logical checks, operators, and the flow of the code, watching `if`, `typeof`, `in`, `instanceof`, and other checks. **Key point:** when TypeScript excludes every possible option, the variable gets the type `never` - this is the foundation for discriminated unions and safe `if`/`switch` statements without extra casts.Shown above the full answer for quick recall.Answer (EN)Image## What is Type Narrowing **Type narrowing** is the process by which TypeScript **refines** a variable's type based on logical checks, operators, and the flow of the code. > In simpler terms: > TypeScript "watches" your `if`, `typeof`, `in`, `instanceof`, and other checks, > and **figures out** which type is actually possible in each branch of the code. --- ### Example: without narrowing ```javascript function printId(id: string | number) { console.log(id.toUpperCase()); // Error - id can be a number } ``` `id` has the type `string | number`, and TypeScript does not know which one is currently in use. --- ### Example with narrowing ```javascript function printId(id: string | number) { if (typeof id === "string") { // Here id: string console.log(id.toUpperCase()); } else { // Here id: number console.log(id.toFixed(2)); } } ``` TypeScript has **narrowed** the type of `id`: `string` in the first branch, `number` in the second. --- ## How narrowing works TypeScript performs **control flow analysis** and automatically "cuts off" impossible types when it encounters: - checks with `typeof`, `instanceof`, `in`, `===`, `!==`, `==`, `!=`; - checks against `null` / `undefined`; - custom **type guards** (`value is Type`); - logical statements (`if`, `else`, `switch`, `return`, `throw`, etc.). --- ## The main ways to narrow types | Method | Description | Example | |---|---|---| | `typeof` | For primitives | `typeof x === "string"` | | `instanceof` | For classes and constructors | `x instanceof Date` | | `in` | Checks whether a property exists | `"prop" in obj` | | `===` / `!==` | Comparison of literal values | `x === null` | | `null` / `undefined` checks | Excludes "empty" values | `if (x)` | | Custom type guard | A function of the form `x is T` | `isArray(value): value is any[]` | --- ## Examples of different narrowing variants ### 1. `typeof` - primitives ```javascript function log(value: string | number | boolean) { if (typeof value === "string") { value.toUpperCase(); // string } else if (typeof value === "number") { value.toFixed(2); // number } else { value.valueOf(); // boolean } } ``` --- ### 2. `instanceof` - classes and objects ```javascript function logDate(d: Date | string) { if (d instanceof Date) { d.toISOString(); // Date } else { d.toUpperCase(); // string } } ``` --- ### 3. `in` - checking whether a property exists ```javascript type Dog = { bark: () => void }; type Cat = { meow: () => void }; function speak(animal: Dog | Cat) { if ("bark" in animal) { animal.bark(); // Dog } else { animal.meow(); // Cat } } ``` --- ### 4. `== null` checks ```javascript function greet(name?: string | null) { if (name == null) { console.log("No name provided"); } else { console.log("Hello, " + name.toUpperCase()); } } ``` After `if (name == null)` TypeScript narrows the type to `null | undefined`, and after `else`, to `string`. --- ### 5. With a custom type guard ```javascript function isString(value: unknown): value is string { return typeof value === "string"; } function print(value: unknown) { if (isString(value)) { value.toUpperCase(); // now definitely a string } } ``` > Custom type guards are a way to teach TS to "recognize" your own types. --- ### 6. With literal types and comparisons ```javascript type Status = "loading" | "success" | "error"; function handle(status: Status) { if (status === "loading") { // status: "loading" } else if (status === "success") { // status: "success" } else { // status: "error" } } ``` This is a special case of **discriminated union narrowing** (narrowing by a discriminant property). --- ## Narrowing in union types (Union Narrowing) If a variable has the type `A | B | C`, TypeScript will **narrow** it to a specific subtype once a condition rules out the rest. ```javascript function example(x: string | number | boolean) { if (typeof x === "string") { // string } else if (typeof x === "number") { // number } else { // boolean } } ``` --- ## Narrowing to `never` When TypeScript rules out every possible option, the variable gets the type `never` (an unreachable type). ```javascript function process(value: string | number) { if (typeof value === "string") { return value.toUpperCase(); } else if (typeof value === "number") { return value.toFixed(2); } else { // value: never (this branch cannot be reached) throw new Error("Unexpected type"); } } ``` --- ## Why this matters **Safety:** TS will not let you call a method that does not belong to the type. **Convenience:** The IDE suggests the available properties and methods after narrowing. **Clear code:** You can write clean `if`/`switch` statements without unnecessary casts (`as`). **The basis of discriminated unions:** Without narrowing, TS would not be able to tell which variant of a union type is in use. --- ## Summary | Concept | Description | |---|---| | **Type narrowing** | The process of refining a variable's type based on conditions | | **Why it is needed** | So TypeScript knows which subtype is actually in use at a given point | | **When it triggers** | On checks (`typeof`, `instanceof`, `in`, `===`, `is`, `null`, etc.) | | **Result** | TS "narrows" a union or unknown type down to a specific one | | **The "never" type** | Used when no other option remains | --- **Just remember:** > *Type narrowing* is when TypeScript "thinks like a human": > it reads your `if` and **figures out** what type the value has inside each branch.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.