Repository: TypeScript
Issue: Create flag strictVariableInitialization
Labels: Suggestion,Awaiting More Feedback

### 🔍 Search Terms

uninitialized variable, undefined, closure, strict property initialization, 

### ✅ 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

I would like to see a check that ensures that no variable whose type is not permitted to be `undefined` may remain uninitialized at the end of its scope.

### 📃 Motivating Example

TypeScript allows unsafety that can and does catch users by surprise when using variables in a closure. Normally, TS won't let you access an uninitialized variable:

```ts
function doSomething() {
   let foo: string;
   foo.toLowerCase(); // TS ERROR: Variable 'foo' is used before being assigned 
}
```

However, TypeScript optimistically assumes that variables are initialized when used in closures.

```ts
let foo: string;

function printFoo() {
    console.log(foo.toLowerCase());
}

printFoo(); // Uncaught TypeError: Cannot read properties of undefined (reading 'toLowerCase')
```

That's for good reason, but sometimes, as in the above case, this is _provably_ unsafe, since `foo` is guaranteed not to be initialized.

The new flag "strictVariableInitialization" ensures that a variable _must_ be initialized by the end of all reachable codepaths in its scope.

```ts
let foo: string; // (proposed) TS ERROR: `foo` is not initialized in all reachable codepaths 

function printFoo() {
    console.log(foo.toLowerCase());
}

printFoo()
```

Of course, variables whose type includes `undefined` are still permitted to be uninitialized.
```ts
let foo: string | undefined;

function printFoo() {
    console.log(foo?.toLowerCase());
}

printFoo();
```

This check is highly analogous to [strictPropertyInitialization](https://www.typescriptlang.org/tsconfig/#strictPropertyInitialization) for classes.

### 💻 Use Cases

See https://github.com/typescript-eslint/typescript-eslint/issues/9565 for a somewhat-wordier proposal in typescript-eslint, and https://github.com/typescript-eslint/typescript-eslint/issues/4513 and https://github.com/typescript-eslint/typescript-eslint/issues/10055#issuecomment-2374797860 for cases where this has caused confusion in the wild. 

In short, people who have written code that _does_ check for initialization of non-nullable variables become confused by the linter informing them that the check is unnecessary according to the types, even though they can see that it _is_ necessary at runtime:
```ts
let foo: Something

function useFoo() {
   if (foo != null) { // linter flags this condition as unnecessary since foo cannot be nullish
      foo.bar();
   }
}
```

The code _should_ be rewritten as 
```ts
let foo: Something | undefined

function useFoo() {
   if (foo != null) {
      foo.bar();
   }
}
```