Skip to main content

What is type narrowing?

What is Type Narrowing

Type narrowing is a process in which TypeScript understands exactly what type a value has in a particular branch of code and narrows the general type down to a more specific one.

Put simply: If a variable can have several types (for example, string | number), TypeScript "narrows" that set when you perform checks such as typeof, in, instanceof, and so on.


Example 1. Narrowing via typeof

javascript
function printId(id: string | number) { if (typeof id === "string") { // Here id: string console.log("String:", id.toUpperCase()); } else { // Here id: number console.log("Number:", id.toFixed(2)); } }

TypeScript understands on its own:

  • inside if (typeof id === "string") -> id became string
  • inside else -> id became number

Example 2. Narrowing via instanceof

javascript
class User { constructor(public name: string) {} } class Admin { constructor(public name: string, public permissions: string[]) {} } function printPerson(person: User | Admin) { if (person instanceof Admin) { // Here person: Admin console.log("Permissions:", person.permissions); } else { // Here person: User console.log("Regular user:", person.name); } }

Example 3. Narrowing via in (property presence)

javascript
type User = { name: string }; type Admin = { name: string; permissions: string[] }; function printInfo(person: User | Admin) { if ("permissions" in person) { // person: Admin console.log("Permissions:", person.permissions); } else { // person: User console.log("Name:", person.name); } }

in checks for the presence of a property, and TypeScript uses this for logical type narrowing.


Example 4. Narrowing via ==, ===, !==, !=

TypeScript also knows how to "understand" the type after such checks:

javascript
function process(value: string | null) { if (value !== null) { // value: string console.log(value.toUpperCase()); } }

Example 5. Narrowing via "truthy/falsy" values

TypeScript knows that null, undefined, 0, "", false are "falsy" values.

javascript
function printName(name?: string) { if (name) { // name: string console.log("Hello,", name.toUpperCase()); } else { // name: undefined console.log("Name not provided"); } }

Example 6. Custom type checks (Type Guards)

You can create your own function that helps TypeScript narrow a type:

javascript
type User = { name: string }; type Admin = { name: string; permissions: string[] }; function isAdmin(person: User | Admin): person is Admin { return "permissions" in person; } function handle(person: User | Admin) { if (isAdmin(person)) { // Here TS knows that person: Admin console.log("Permissions:", person.permissions); } else { // Here person: User console.log("User:", person.name); } }

person is Admin is a user-defined type guard, which tells TS exactly what type is returned from the check.


Example 7. Narrowing inside a switch

javascript
type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function getArea(shape: Shape): number { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; case "triangle": return (shape.base * shape.height) / 2; } }

Here TypeScript uses a discriminated union and automatically narrows the type in each case branch.


Where type narrowing is used

ScenarioExample check
Primitivestypeof value === "string"
Classesinstanceof SomeClass
Objects"prop" in obj
Comparisonsif (x !== null)
Boolean checksif (value)
Custom guard functionsvalue is Type
switch discriminationcase "circle": …

Why it's needed

Type narrowing lets you:

  • work without errors with union types;
  • get autocomplete and hints in the IDE;
  • write safe and readable code without unnecessary type casts (as).

In short:

Type narrowing is when TypeScript "understands" exactly what type a variable has at a given moment, and automatically narrows the general type down to a specific one across different code branches.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.