Narrowing uncertain values

Narrow with in and instanceof

Use property checks for object unions and constructor checks for real runtime classes.

typeof only distinguishes primitives. Every object answers 'object', so a union of two object shapes needs a different runtime question. You have two: does this property exist, and was this built by that constructor?

Narrow with in

The JavaScript in operator checks whether a property exists on an object or its prototype chain. TypeScript can use that fact to narrow an object union:

type Success = { data: string }
type Failure = { message: string }

function print(result: Success | Failure) {
  if ('message' in result) {
    console.error(result.message)
    return
  }

  console.log(result.data)
}

Inside the if, only Failure has a message property, so result is a Failure. After the early return, TypeScript knows the remaining code deals with Success, and result.data compiles.

Without the check, result.data fails:

Property 'data' does not exist on type 'Success | Failure'.
  Property 'data' does not exist on type 'Failure'.

One caveat: optional properties can appear in both branches because they may exist or be absent. If both variants declare message?: string, the in check proves nothing. A required discriminant is clearer when variants overlap.

Narrow with instanceof

instanceof checks the prototype chain:

function logWhen(value: Date | string) {
  if (value instanceof Date) {
    console.log(value.toISOString())
  } else {
    console.log(value)
  }
}

In the first branch value is a Date, so toISOString() is available. In the else branch it must be a string.

This works for runtime constructors such as Date, Error, URL, and your JavaScript classes. It cannot use a type alias or interface because those disappear during compilation. Writing value instanceof Success fails with:

'Success' only refers to a type, but is being used as a value here.

That error is the compiler reminding you which check you need. When the variants are plain object shapes, reach for in or a discriminant field. When they are class instances, instanceof is the direct, honest check.

Exercise: create a union of Date | string, narrow it with instanceof, and format both branches.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →