# @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.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

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

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

Every option, set to its default:

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

{/* End of the generated section. */}

## Examples

Reported:

{/* bad {"count": 3} */}
```ts
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;
}
```

{/* bad {"count": 2} */}
```ts
function greet(name: string) {
  return name === undefined ? "Hello" : `Hello, ${name}`;
}

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

Not reported:

{/* good */}
```ts
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:

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

## Options

| Option | Default | What it does |
| --- | --- | --- |
| `allowConstantLoopConditions` | `"never"` | Which constant loop conditions are allowed. See below. |
| `checkTypePredicates` | `false` | Also report a call to a type guard or an assertion function if the argument already has that type. |
| `allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing` | `false` | Without `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:

| Value | What it does |
| --- | --- |
| `"never"` or `false` | Report every constant loop condition, including `while (true)`. |
| `"always"` or `true` | Allow 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"`:

{/* good {"options": [{"allowConstantLoopConditions": "only-allowed-literals"}]} */}
```ts
const queue = [1, 2, 3];

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

Reported with `checkTypePredicates`:

{/* bad {"options": [{"checkTypePredicates": true}]} */}
```ts
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.
