@typescript-eslint strict-boolean-expressions check for falsy value

Viewed 10396

I recently had an issue regarding a ternary checking a number | undefined var for undefined, but due to my lack of attention when writing the code, when the number was 0, it wrongly accused it being a undefined value.

Then, I found about about the strict-boolean-expressions ESLint rule.

Looks very useful and safe, but, given this example:

const text: string | undefined = stringOrUndefined1 || stringOrUndefined2 || undefined; // the strings can be empty
if (!text) // I was doing it this way to check if the value was falsy. With the new rule, it complains.
  return;
if (text === undefined || text === '') // This works, but is 4x the length of the one above. I don't want to write the var name more than once
if (!!text == false) // "Unexpected nullable string value in conditional. Please handle the nullish/empty cases explicitly."
if (!!!text) // Same warn as above

Is there any way to quickly and nicely check if the value is falsy without the lengthy second conditional, and without disabling the rule, even just for the specific line?

I know that it's possible to disable just for nullable strings, but this question also applies for numbers, the rule that I want to keep for safety reasons.

3 Answers

You could try using the nullish coalescing operator (??) like this:

if (!(text ?? '')) {}

text ?? '' evaluates to '' if it is undefined and text otherwise.

This is equivalent to the following:

if (!(text !== null && text !== undefined ? text : '')) {}

If you want to check the "truthiness" of a value despite having strict-boolean-expressions enabled, you could define helper functions for the purpose:

type Falsy = 0 | '' | false | null | undefined

function truthy<T>(x: T): x is Exclude<T, Falsy> {
    return !!(x as any);
}

function falsy<T>(x: T): x is T & Falsy {
    return !(x as any);
}

Then you can write if(truthy(str)) { ... } or if(falsy(str)) { ... } where you want to use those tests.

Note that the definition of the Falsy type here is incomplete; for example when T is number then falsy will narrow x to the literal type 0 when NaN is also a possible falsy value. Unfortunately, there is no way around this since Typescript has no NaN literal type; this will only be a problem if you do expect NaN values in your code.

As others have mentioned - the point of strict-boolean-expressions is to help ensure you are explicitly handling each case so that you don't accidentally let certain cases slip through.

@cherryblossom's answer is one way to do it concisely and clearly - it shows you are intentionally handling the nullish and empty string cases the same way.

If your project is okay with doing loose checks on nullish strings - you can reconfigure the rule as appropriate: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/strict-boolean-expressions.md#allownullablestring


As an aside - personally I don't see the problem with just typing the extra few characters in the check. The clear and explicit handling is good for your codebase, and ultimately it's only a handful of extra keystrokes.

Most IDEs (like VSCode) have the ability to add templated snippets if you're so inclined to save keystrokes.

Related