Be careful with truthy promises in TypeScript

There’s a type of small “gotcha” bug that is easy to hit with promises for boolean values in JavaScript.

The simplest example might be this:

async function checkSomeCondition(): Promise<boolean> {
        return false
}

if (checkSomeCondition()) {
        // do something.
}

The if branch will always be executed, because the promise object returned by checkSomeCondition() will be evaluated as true, regardless of what it would resolve to. It’s a typo-level mistake, but is easy to make.

It’s easier to miss this mistake when the condition has more parts, e.g.

firstCheck() || secondCheck() || (firstCompound() && secondCompound())

Perhaps some or all of those are actually promises; it’s not immediately obvious at first glance.

The problem itself can be fixed by chucking an await into the if condition:

if (await checkSomeCondition()) {
        // do something.
}

However, if you’re paying enough attention to add the await, then you probably won’t make this mistake in the first place.

TypeScript can help out if you’re in the habit of adding explicit type annotations:

const someCondition: boolean = checkSomeCondition()
if (someCondition === true) {
        // do something.
}

Now the compiler will complain if you miss the await on checkSomeCondition(), and you can err on the side of caution by only accepting a real boolean true at runtime as well.

The no-floating-promises and strict-boolean-expressions rules for tslint will also help catch this mistake.


Tech mentioned