Skip to content
NewBun 1.4.5 is out. Release notes
AphrodyBun

Linter · Rules

@typescript-eslint/no-unnecessary-condition

Disallow conditions that the types say are always true or always false

If the types say user is always an object, if (user), user?.name and user ?? fallback test for something that can't happen. Either the check is dead code, or the type is wrong and hides a real case.

The rule checks if, loops, ? :, &&, ||, ??, ?., switch cases, comparisons with null and undefined, comparisons of two literal types, and the callbacks of array methods such as filter. Values of type any or unknown and type parameters without a constraint are never reported.

Configure

Off by default. This rule needs types, so turning it on makes bun lint type check your code first.

bunfig.toml
[lint.rules]
"@typescript-eslint/no-unnecessary-condition" = "error" # or "warn"

Every option, set to its default:

bunfig.toml
[lint.rules]
"@typescript-eslint/no-unnecessary-condition" = ["error", {
  # true, false, "always", "never" or "only-allowed-literals"
  allowConstantLoopConditions = "never",
  checkTypePredicates = false,
  allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing = false,
}]

Examples

Reported:

interface User {
  name: string;
  tags: string[];
}

function label(user: User) {
  if (user.tags) {
    console.log(user.tags.length);
  }
  const name = user.name ?? "anonymous";
  return user?.name + name;
}
function greet(name: string) {
  return name === undefined ? "Hello" : `Hello, ${name}`;
}

const users = [{ name: "Ada" }, { name: "Alan" }];
const named = users.filter(user => user);

Not reported:

interface User {
  name?: string;
  tags: string[];
}

function label(user: User | undefined) {
  if (user?.tags.length) {
    console.log(user.tags.length);
  }
  return user?.name ?? "anonymous";
}

An element read from an array by index isn't reported, because the index can be out of range even though the type doesn't say so:

function firstId(items: { id: number }[]) {
  if (!items[0]) {
    return -1;
  }
  return items[0].id;
}

Options

OptionDefaultWhat it does
allowConstantLoopConditions"never"Which constant loop conditions are allowed. See below.
checkTypePredicatesfalseAlso report a call to a type guard or an assertion function if the argument already has that type.
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoingfalseWithout strictNullChecks in tsconfig.json, the rule can't tell what can be null, and it reports that once for each file. Set this to true to turn that report off.

allowConstantLoopConditions takes one of these:

ValueWhat it does
"never" or falseReport every constant loop condition, including while (true).
"always" or trueAllow any loop condition of type true.
"only-allowed-literals"Allow only true, false, 0 and 1 written in the condition itself.

Not reported with "only-allowed-literals":

const queue = [1, 2, 3];

while (true) {
  if (queue.pop() === undefined) break;
}

Reported with checkTypePredicates:

declare function isString(value: unknown): value is string;

function print(name: string) {
  if (isString(name)) {
    console.log(name);
  }
}

When not to use it

The rule trusts your types. If much of your data comes from outside and is typed without being validated, such as the result of JSON.parse cast to an interface, it reports defensive checks you want to keep.