Repository: TypeScript
Issue: Treat `expr satisfies never;` similarly to a call to a never-returning function in CFA
Labels: Suggestion,In Discussion

### 🔍 Search Terms

Control flow analysys, CFA, satisfies, never, return, termination, assert

### ✅ Viability Checklist

- [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
- [x] This wouldn't change the runtime behavior of existing JavaScript code
- [x] This could be implemented without emitting different JS based on the types of the expressions
- [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- [x] This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- [x] This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals

### ⭐ Suggestion

In CFA, `expr satisfies never;` statements should be treated similarly to a call to a never-returning function, treating statements that follow as unreachable.

I'm suggesting that TS enhance the `expr satisfies never;` pattern which is one of the ways to do the **exhaustiveness check**. It is a very popular technique used when dealing with discriminated union types. The ways often used are:

```ts
// 1-a. assign to a never-typed variable and abort
const _exhaustivenessCheck: never = option;
throw new Error("unreachable");

// 1-b. return never instead of abort
const _exhaustivenessCheck: never = option;
return _exhaustivenessCheck;

// 2. Call a function that accepts never
assertNever(option);

// 3-a. Use the satisfies syntax and abort
option satisfies never;
throw new Error("unreachable");

// 3-b. Use the satisfies syntax and return
option satisfies never;
return option;
```

Currently, 1 and 3 requires either throwing or returning to terminate the current (unreachable) execution branch. On the other hand, 2 automatically terminates it (assuming that the return type of `assertNever` is `never`).

My suggestion is that 3 (`option satisfies never`) also work as a termination point of the current execution branch, so that the following would be possible:

```ts
// 3-c (suggested behavior). Use the satisfies syntax and that's it!
option satisfies never;
/* code here is considered unreachable */
```

I think `expr satisfies never;` has several advantages compared to others:

- Doesn't involve unused variables. Unused variables are annoying especially when using linters.
- Is completely no-op at runtime, which [allows minifiers to optimize away more code](https://play.swc.rs/?version=1.10.8&code=H4sIAAAAAAAAA%2BVSS26DMBDdI3GHJ9RF2FRql6TNEZJFOAAuHRIrwUb2EFpR7l6wTeVFK7XrLmd4835Gtp02jBHCWjK8pxsZTGiMbpFZ3dJwJkPZNk34vSMcOpZaPZU7PGNME4DFqUB2nIELBriJa08FynmY8BFj9lo5zJQmaUJvTrbpVb0QQjveUh%2FZSHV62Pi5WPV6dVF6ULu8gHUIT3wlXgX92lmwg%2BT6jMBxP6vnHg7UwlJwW6y74HkOVC0fNndjOHTrKa%2B2K%2B7FkLiEaYoJXbRvCL8y%2F3j%2FSo3orxzdenFYwdI2kizU8iTxlSsQMMS9Uah8RbPtyO8vOn78xx1H%2F3pIkP%2B94E%2FGywblOgMAAA%3D%3D&config=H4sIAAAAAAAAA32UO3LjMAxA%2B5zCo3rbTZEDpMsZOLQIysxShIYAHWsyvntAfWxvDKmT8PAjft8vh0PzSW3zdviWT%2FkZbCbIt3%2BR0JjYXkTS8DgAtTkM3PxZKVNF3kaCSXSdScM2d8DVCujvot5ERIJVfZH1IQU%2FPgZssR8yED3IRCr%2BSg%2BJ6X%2F7hWX8qoBzeZQfESPYtEOMJRMSQwdZc9xijHYgMGebFS81U5sDoRaiwsLgzJBxUHlygQMmiflMHVhnWnSgoJCh5XAGzUxiiVkieZ7yngk7OJaum5r8yxrONhbLSky4TC2RbBWvJwzExpeklXCGGzWY4VLc35bBmwxccnq2%2B8SQNnryD0AqEC1Rsj1oficNL%2FO0Ze13LUPyMrI8KlzmW3tlgk6KakLwSmVrZSBz0LqZwZUWamVbLZ0Fb5SPggMD3susKK7pK3B70oLWLUevAOmv9dpUzcDctnCD14XYwe%2FyStYHbNHoLZ%2B2KY39EeNOgB74hG5HQVrBuI2zXInLsM1LciCjAU5VKTSB5yMgC8Bo4nQsn2ZD1kM8mi7i8X4mFoXr7Qj3NnX3fZ%2Fv8Mui0PToygSXC1%2F7O9%2Fl1%2BautF7hNYMm0MdqOMW8%2FgBp5ICPLAYAAA%3D%3D).

Also, notably, `expr satisfies never;` is explicit enough for the compiler to reason about as part of the CFA.

In addition, I think this is also good from a theoretical perspective; `never`-returning functions are currently treated as a termination point of an execution branch because such functions can _never_ return anything. Actually, this is not specific to functions. If you somehow have a value of type `never`, you have proven that this code isn't actually executed. `expr satisfies never;` feels like a good, sensible way to declare that you have a proof.

### 📃 Motivating Example

```ts
type Option<T> = {
  tag: "Some";
  value: T;
} | {
  tag: "None";
}


function optionToString(option: Option<unknown>): string {
  let value: string;
  switch (option.tag) {
    case "Some": {
      value = `Some(${option.value})`;
      break;
    }
    case "None": {
      value = "None";
      break;
    }
    default: {
      option satisfies never;
      // ↑ should have the same effect as:
      // assertNever(option);
    }
  }

  // Current behavior: error because `value` may not have been assigned.
  // Suggested behavior: no error here.
  return `Option(${value})`;
}

function assertNever(value: never): never {
  throw new Error("unreachable");
}
```

[Playground](https://www.typescriptlang.org/play/?#code/C4TwDgpgBA8mwEsD2A7APAFQHxQLxQG8AoKKYAQwHMAuKAIgGUkBbCOgbhKgDdyAbAK4RaGTgF8oAH0JcKNegDlUbcUTUAzASgDGiVFCTxkKDEgbAATghSUAFIb0pacR2i0BrFEgDuKLAEpaAGdLa0oZUj4IYB5+IWDQm05SIO8EYG0ACyh7I1QAOjl-CNIobXIg6EYWNlpiUtLeQWh8AAMmVlsAEgIHY3ymoTF-VuSGqAAjCwhydzHSMS5ScsrFZTo6pca4lrWUFS3SKZm5rcWGgBMIdXIBPmBN8YM8lCgg8kQg9QQIIKh97gQCzzKDnUFqUjTYACCyvVouYzdAiDCDDUZERZEbSoIJIKL5PhIOx9VCmcxWGy2AhkKi0aqsOgAGlizVoAEYAEwAZlB-n8nGxKFx+MJxJeZMSdmpcjpSn2dF5-I0Wl0xigFUqFmACgggIsthRtABQMC-11QJKwEyFh8Zu8UAAohYbfq6FppuQsuQJlE6ErFkA)

### 💻 Use Cases

#### 1. What do you want to use this for?

As shown above, for exhaustiveness checks.

#### 2. What shortcomings exist with current approaches?

Live with less optimal (from different aspects) ways.

#### 3. What workarounds are you using in the meantime?

```ts
assertNever(option);
```