# @typescript-eslint/switch-exhaustiveness-check

> Require a `switch` on a union or an enum to have a `case` for every member

When you add a member to a union or an enum, every `switch` on it needs a new `case`. TypeScript doesn't tell you which ones. The new value falls through the `switch` and nothing happens, or it lands in a `default` that was written for something else.

This rule reports a `switch` on a union of literals or an enum that doesn't list every member, and names the ones that are missing. By default a `default` clause doesn't count as covering them.

{/* 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/switch-exhaustiveness-check" = "error" # or "warn"
```

Every option, set to its default. The ones that are commented out are unset by default.

```toml bunfig.toml
[lint.rules]
"@typescript-eslint/switch-exhaustiveness-check" = ["error", {
  considerDefaultExhaustiveForUnions = false,
  allowDefaultCaseForExhaustiveSwitch = true,
  requireDefaultForNonUnion = false,
  # defaultCaseCommentPattern = <string>,
}]
```

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

## Examples

Reported:

{/* bad */}
```ts
type Status = "open" | "closed" | "merged";

function label(status: Status) {
  switch (status) {
    case "open":
      return "Open";
    case "closed":
      return "Closed";
  }
}
```

Not reported:

{/* good */}
```ts
type Status = "open" | "closed" | "merged";

function label(status: Status) {
  switch (status) {
    case "open":
      return "Open";
    case "closed":
      return "Closed";
    case "merged":
      return "Merged";
  }
}
```

## Options

| Option | Default | What it does |
| --- | --- | --- |
| `considerDefaultExhaustiveForUnions` | `false` | Let a `default` clause cover the members that have no `case`. |
| `allowDefaultCaseForExhaustiveSwitch` | `true` | With `false`, report a `default` clause on a `switch` that already lists every member. |
| `requireDefaultForNonUnion` | `false` | Require a `default` clause when the value isn't a union of literals, such as a `string` or a `number`. |
| `defaultCaseCommentPattern` | `^no default$`, ignoring case | A regular expression. A comment after the last `case` that matches it counts as a `default` clause. |

By default `"closed"` and `"merged"` are reported as missing here:

{/* bad */}
```ts
function isOpen(status: "open" | "closed" | "merged") {
  switch (status) {
    case "open":
      return true;
    default:
      return false;
  }
}
```

With `considerDefaultExhaustiveForUnions`, the `default` covers them:

{/* good {"options": [{"considerDefaultExhaustiveForUnions": true}]} */}
```ts
function isOpen(status: "open" | "closed" | "merged") {
  switch (status) {
    case "open":
      return true;
    default:
      return false;
  }
}
```

A `default` on a complete `switch` hides the next member you add. With `allowDefaultCaseForExhaustiveSwitch` off, it's reported:

{/* bad {"options": [{"allowDefaultCaseForExhaustiveSwitch": false}]} */}
```ts
function isOpen(status: "open" | "closed") {
  switch (status) {
    case "open":
      return true;
    case "closed":
      return false;
    default:
      throw new Error("unknown status");
  }
}
```

With `requireDefaultForNonUnion`, a `switch` on a `number` needs a `default`:

{/* bad {"options": [{"requireDefaultForNonUnion": true}]} */}
```ts
function describe(code: number) {
  switch (code) {
    case 404:
      return "Not found";
  }
}
```

A `// no default` comment says that you left it out on purpose:

{/* good {"options": [{"requireDefaultForNonUnion": true}]} */}
```ts
function log(code: number) {
  switch (code) {
    case 404:
      console.log("Not found");
    // no default
  }
}
```
