if (count) on a number | undefined is false for undefined, and also for 0 and NaN. if (name) on an optional string is also false for "". Code that means "was it given?" then skips a valid value.
This rule checks the conditions of if, while, do, for, and ? :, the operand of !, the left side of && and ||, and the return value of callbacks passed to filter, find, some, every, and similar array methods.
By default a condition can be a boolean, a string, a number, or an object that can be null or undefined. These are reported:
- a
boolean,string,number, or enum that can also benullorundefined anyandunknown- a mix such as
string | number - an object that's never nullish, which is always true
- a value that's always nullish, which is always false
This rule needs strictNullChecks (or strict) in your tsconfig.json.
Configure
Off by default. This rule needs types, so turning it on makes bun lint type check your code first.
[lint.rules]
"@typescript-eslint/strict-boolean-expressions" = "error" # or "warn"
Every option, set to its default:
[lint.rules]
"@typescript-eslint/strict-boolean-expressions" = ["error", {
allowString = true,
allowNumber = true,
allowNullableObject = true,
allowNullableBoolean = false,
allowNullableString = false,
allowNullableNumber = false,
allowNullableEnum = false,
allowAny = false,
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing = false,
}]
Examples
Reported:
function render(width?: number, title?: string, items: string[] = []) {
if (width) {
resize(width); // skipped for 0
}
const heading = title || "Untitled"; // replaces ""
if (items) {
draw(items); // always runs, even for []
}
}
declare function resize(width: number): void;
declare function draw(items: string[]): void;
Not reported:
function render(width?: number, title?: string, items: string[] = []) {
if (width !== undefined) {
resize(width);
}
const heading = title ?? "Untitled";
if (items.length > 0) {
draw(items);
}
}
declare function resize(width: number): void;
declare function draw(items: string[]): void;
Array method callbacks, unknown, and mixed types are reported too:
function pick(sizes: (number | undefined)[], raw: unknown, id: string | number) {
const known = sizes.filter(size => size); // drops 0 too
if (raw) console.log(raw);
if (id) console.log(id);
}
A nullable object has only one falsy case, so it's allowed:
function nameOf(user?: { name: string }) {
return user ? user.name : "guest";
}
Options
| Option | Default | What it does |
|---|---|---|
allowString | true | Allow string. |
allowNumber | true | Allow number and bigint. |
allowNullableObject | true | Allow an object, function, or symbol that can be nullish. |
allowNullableBoolean | false | Allow boolean | null | undefined. |
allowNullableString | false | Allow string | null | undefined. |
allowNullableNumber | false | Allow number | null | undefined. |
allowNullableEnum | false | Allow an enum that can be nullish. |
allowAny | false | Allow any and unknown. |
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing | false | Don't report a project that has strictNullChecks off. Without it TypeScript hides null and undefined from types, so the rule misses most cases. |
Turn off allowString and allowNumber to accept only real booleans and nullable objects. These are then reported:
function summary(items: string[], query: string) {
if (items.length) {
console.log(items.length);
}
return query ? `Results for ${query}` : "All";
}
An optional flag is a common case to allow. Use allowNullableBoolean for that:
function build(options: { minify?: boolean }) {
if (options.minify) {
console.log("minifying");
}
}
When not to use it
In a codebase that relies on truthiness checks, this rule reports a lot of code that works. Start with the allowNullable options on, or leave it off.