Skip to content

noUnnecessaryConditions

biome.json
{
"linter": {
"rules": {
"suspicious": {
"noUnnecessaryConditions": "error"
}
}
}
}

Disallow conditions that always evaluate to the same value.

Using type information, this rule reports conditions whose result is statically known. It covers if/while/for/ternary tests, the ?? and ||/&& operators, optional chaining (?.), comparisons against null/undefined, and case clauses that can never match the value passed to switch.

A non-nullable value never needs an if guard:

function head<T>(items: T[]) {
if (items) {
return items[0];
}
}
code-block.ts:2:9 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This condition is always truthy.

1 │ function head<T>(items: T[]) {
> 2 │ if (items) {
^^^^^
3 │ return items[0];
4 │ }

The value’s type can never be falsy, so this check is redundant.

A literal-union type can never be empty, so the truthiness check is redundant:

function foo(arg: 'bar' | 'baz') {
if (arg) {}
}
code-block.ts:2:9 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This condition is always truthy.

1 │ function foo(arg: ‘bar’ | ‘baz’) {
> 2 │ if (arg) {}
^^^
3 │ }
4 │

The value’s type can never be falsy, so this check is redundant.

?. and ?? on operands that are guaranteed to be non-nullish:

function bar(arg: string) {
return arg?.length;
}
code-block.ts:2:12 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Unnecessary optional chaining.

1 │ function bar(arg: string) {
> 2 │ return arg?.length;
^^^
3 │ }
4 │

Replace ?. with ..

1 │ function bar(arg: string) {
> 2 │ return arg?.length;
^^
3 │ }
4 │

The receiver is guaranteed to be non-nullish.

function withDefault(name: string) {
return name ?? "anonymous";
}
code-block.ts:2:12 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Unnecessary nullish coalescing.

1 │ function withDefault(name: string) {
> 2 │ return name ?? “anonymous”;
^^^^
3 │ }
4 │

Drop ?? and the fallback expression.

1 │ function withDefault(name: string) {
> 2 │ return name ?? “anonymous”;
^^
3 │ }
4 │

The left-hand side is guaranteed to be non-nullish, so the fallback is unreachable.

|| and && on always-truthy operands:

interface Config { items: string[] }
function f(c: Config) {
return c.items || [];
}
code-block.ts:3:12 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This condition is always truthy.

1 │ interface Config { items: string[] }
2 │ function f(c: Config) {
> 3 │ return c.items || [];
^^^^^^^
4 │ }
5 │

The value’s type can never be falsy, so this check is redundant.

!expr on a value that is always truthy:

const items = [];
if (!items) {}
code-block.ts:2:5 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This condition is always falsy.

1 │ const items = [];
> 2 │ if (!items) {}
^^^^^^
3 │

The value’s type can never be truthy, so this check is redundant.

Comparing a non-nullable value against null or undefined:

function f(x: string) {
return x === null;
}
code-block.ts:2:12 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This comparison always has the same result.

1 │ function f(x: string) {
> 2 │ return x === null;
^^^^^^^^^^
3 │ }
4 │

The operands’ types make the outcome statically known.

A case whose value can never equal the value passed to switch:

function f(v: 'a' | 'b') {
switch (v) {
case 'c': return 1;
}
}
code-block.ts:3:14 lint/suspicious/noUnnecessaryConditions ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

This case is unreachable.

1 │ function f(v: ‘a’ | ‘b’) {
2 │ switch (v) {
> 3 │ case ‘c’: return 1;
^^^
4 │ }
5 │ }

The value passed to switch can never equal this value.

When the type allows nullish or empty values, the check is meaningful. The rule also does not report bindings that are reassigned to values of different truthiness, since their narrowing cannot be inferred reliably.

function head<T>(items: T[] | null) {
if (items) {
return items[0];
}
}
function bar(arg: string | undefined) {
return arg?.length;
}
function f(v: 'a' | 'b' | 'c') {
switch (v) {
case 'a': break;
case 'b': break;
case 'c': break;
}
}
let greeting = false;
function update() { greeting = "Hello"; }
if (greeting) {}