ProgrammingFrontend Developer

Explain the mechanics of type narrowing in TypeScript. What are the ways to narrow variable types, and what is it used for?

Pass interviews with Hintsage AI assistant

Answer.

Type Narrowing in TypeScript is the process where the compiler understands that in a specific code block, a variable undoubtedly has a more specific type based on certain conditions.

Typical narrowing techniques include:

  • Checking with the typeof operator:
function example(x: number | string) { if (typeof x === 'string') { // x: string here return x.toUpperCase(); } else { // x: number here return x.toFixed(2); } }
  • Checking with instanceof (for classes):
if (dateObj instanceof Date) { // dateObj: Date }
  • Checking for null and undefined:
function print(value?: string) { if (value != null) { // value: string console.log(value.length); } }
  • Checking using "discriminant properties":
type Pet = { kind: 'dog'; woof: () => void } | { kind: 'cat'; meow: () => void }; function sound(pet: Pet) { if (pet.kind === 'dog') { pet.woof(); } else { pet.meow(); } }

TypeScript also supports user-defined type guard functions:

function isString(x: unknown): x is string { return typeof x === 'string'; }

Narrowing makes type checking safer and the code more reliable.

Trick Question.

Can you guarantee type narrowing through normal comparisons (e.g., ==/===), and does it always work?

Answer: No. TypeScript does not understand the type from simple comparisons in all cases, especially if the comparison is too "vague" or involves indirect variables/properties. Narrowing often requires explicit mechanisms (typeof, instanceof, discriminant properties, and type guards).

Example:

function foo(x: number | string | null) { if (x) { // x: string | number, null is no longer possible, but narrowing to a specific type will not occur } }

History

In a large TypeScript project, the condition user.role == 'admin' did not narrow the data type, and there still needed to be checks for property existence. Developers underestimated the narrowing rules, leading to "Cannot read property ... of undefined" errors.


History

In a mobile application, a function accepted either an object or a string. Due to an indirect function call that changed the type, narrowing did not occur, and there was a crash on some devices when calling a method that was absent on the string. Tests failed on rare cases.


History

When migrating code from JavaScript to TypeScript, user-defined type guard functions were not implemented, assuming that property checks would always narrow the type. As a result, complex objects with optional fields behaved incorrectly, leading to unpredictable runtime data access errors.