Suggest an editImprove this articleRefine the answer for “What does "union narrowing" mean?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Union narrowing** is narrowing a union type down to one or more specific members based on checks in the code, such as `typeof`, `instanceof`, `in`, or custom type guards. **Key point:** thanks to narrowing, TypeScript figures out the exact subtype in each branch on its own, so you don't have to write `as Type` by hand.Shown above the full answer for quick recall.Answer (EN)Image## What a *union type* is First, a reminder: a **union type** (`A | B | C`) is a type that can be **one of several options**. ```javascript type Result = string | number; ``` A variable of type `Result` can be **a string or a number**: ```javascript let value: Result; value = "hello"; // string value = 42; // number ``` But until TypeScript knows **what exactly is inside right now**, it will not let you use type-specific properties. ```javascript value.toFixed(); // Error - could be a string ``` --- ## What *narrowing* is **Narrowing** is the process by which TypeScript **reduces the set of possible types**, based on checks in the code. --- ## What *union narrowing* is **Union narrowing** is narrowing a type **from a union** down to **one or more specific members of that union**, depending on logical conditions, operators, or checks. --- ### Example 1. Narrowing with `typeof` ```javascript function printValue(value: string | number) { if (typeof value === "string") { // here value: string console.log(value.toUpperCase()); } else { // here value: number console.log(value.toFixed(2)); } } ``` TypeScript "narrowed" `value` from `string | number` down to `string` in one branch and `number` in the other. --- ### Example 2. Narrowing with an `in` check ```javascript type Cat = { kind: "cat"; meow: () => void }; type Dog = { kind: "dog"; bark: () => void }; function makeSound(animal: Cat | Dog) { if ("meow" in animal) { // animal: Cat animal.meow(); } else { // animal: Dog animal.bark(); } } ``` --- ### Example 3. Narrowing through a literal property ```javascript type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number }; function area(shape: Shape) { if (shape.kind === "circle") { // shape: { kind: "circle"; radius: number } return Math.PI * shape.radius ** 2; } else { // shape: { kind: "square"; side: number } return shape.side ** 2; } } ``` This is an example of **discriminated union narrowing** (narrowing by the discriminant, the `kind` key). --- ### Example 4. Narrowing with `instanceof` ```javascript function logDate(d: Date | string) { if (d instanceof Date) { // d: Date console.log(d.toISOString()); } else { // d: string console.log(d.toUpperCase()); } } ``` --- ### Example 5. Narrowing with an `== null` check ```javascript function greet(name?: string | null) { if (name == null) { // name: null | undefined console.log("No name provided"); } else { // name: string console.log("Hello, " + name); } } ``` --- ### Example 6. Narrowing with custom type guards You can declare your own **"type filter"** - a function that returns `value is ...`: ```javascript type Car = { wheels: 4; drive: () => void }; type Bike = { wheels: 2; pedal: () => void }; function isCar(v: Car | Bike): v is Car { return (v as Car).drive !== undefined; } function move(vehicle: Car | Bike) { if (isCar(vehicle)) { // vehicle: Car vehicle.drive(); } else { // vehicle: Bike vehicle.pedal(); } } ``` --- ## What TypeScript takes into account during narrowing TypeScript can automatically **analyze control flow**: - `if`, `else`, `switch` - `typeof` - `instanceof` - `in` - `===`, `!==`, `==`, `!=` - `null` and `undefined` checks - custom guards (`v is T`) - `return` / `throw` / `break` (affect which branches remain possible) --- ## An example of combined narrowing ```javascript function process(val: string | number | boolean | null) { if (typeof val === "string") { // val: string } else if (typeof val === "number") { // val: number } else if (val === null) { // val: null } else { // val: boolean (the remaining type) } } ``` --- ## Why this matters Without narrowing, TypeScript could not guarantee type safety. It would not know which type is in use at a given point in the program, and would force you to write `as Type` everywhere. Thanks to **union narrowing**, TypeScript can **analyze the logic of the code** and automatically pick the correct subtype. --- ## Summary | Term | Meaning | |---|---| | **Union Type** | A union of possible types (`A | | **Narrowing** | Reducing the set of possible types based on conditions | | **Union Narrowing** | Narrowing specifically within a union of types | | **How it happens** | Through `typeof`, `instanceof`, `in`, literal checks, guards | | **Main goal** | Letting TypeScript know exactly which type is used in each branch of the code |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.