Repository: TypeScript
Issue: Type reverts to declaration type inside loop
Labels: Bug,Help Wanted

# Bug Report

### 🔎 Search Terms

* type revert
* type for loop wrong
* type narrowing loop

### 🕗 Version & Regression Information

Happens in versions 3.3.3333 to 4.8.0-dev.20220613 (latest nightly). I couldn't test prior versions as that's the oldest the playground will go.

### ⏯ Playground Link

[Playground Link](https://www.typescriptlang.org/play?ts=4.8.0-dev.20220613#code/JYOwLgpgTgZghgYwgAgBoFkAyA5A9gExQG8BYAKAEhyBfc8genuQHkwALaZGAVxATGC4QAZ2Rs4ANxTsUwuAFtZwAOYg4YblAgAaZACNuYZMCPBRIXEZAQIhfMjC5kWgA4AbYAnXS2Z48OFuCHIePgEhZEQkAIBBKCg4AE8ACgtCAC4MHAIdZAQtbwBJGDwwAFEADzMwYXSwKCCASkysPEIAbhDefkEQSIRo4TiElLSIFuzCXXyIIpLLSuravVxcN1mQZqy2iAAfXkIYUFtOslCeiKiIWPik1JyJnemCyGLSxeEa9JW1jeQAXi4cDcwggW1aOX2IEOx3spEoWg0UD6BwgR2s+FOtDIXTCvX6gwAwupgbhlGVwFAUjM5u8qp9lqt1nBNo9Iaj0bZkPCKIjNCjoWjYVi6GduuE+lcAgAxYAQNz4ABqwKCyRpr3m5XpXx+zNZ23Zgs5cPIFHWRgQmgBBOuwmJYFJ5Mp1JeEDeC21wkapwowBgyQAhJaoI0ebyIEiBTCMT7sVRKIxkDEjOw-C5cKAwNMrX4DYRTaaYLgoMlzcZrQAGdrlgA8yAArNWANRN4Ch00URMAIUMYmguWD-jQEMIUOjtg7ZbA8hc1qlQ1u1M0z1mGrpS29ooonaYABEIAg3HAoKBlA4OA5Ei4ULgYA4Zw4nHoUHm9hzYbojhVrue-GZAsElDxtufqBtOLjtkB4aRsg74xh2cbARQg6AuB1aJgAKr4ogeNYQ4AO7iGAADkogIEIPDCKe54oORM7AOsUCmnGpp8sieSaCKOJinilwDLasrykqKoQAA+vhxYANbHrgBwAIxqq67paks3xMhs4KTG+RqwtypplsGolzvxAT2o6FL1C6q5upqHw1JulCgUGmiiZB25sVGQrwZQLEJkwya-qI6aZtmUBGbmI6ATuyBYX4uEoPhDFuMgEAgEWUBIDRl7XggHAIJJj7IJJFj4VlYBXigEVacYIjAIQWVuKss6OLg+kRhxUDWoZPqFsWpbtcAlbVoNdaNsgLZtmGU4PoC87DHcwYrrSHobj6SFOeBbnuRG-KwTp3nbohHYofeLixgWCI7exwZcUAA)

### 💻 Code

```ts
interface XMLNode {
	
}

// Other functions have the same signature, but it is not needed to replicate this issue
function accessArray(node:XMLNode, createIfNotExists:true):XMLNode;
function accessArray(node:XMLNode, createIfNotExists:boolean):XMLNode|undefined;
function accessArray(node:XMLNode, createIfNotExists:boolean = false):XMLNode|undefined {
	return undefined;
}

function accessCatalogEntry(createIfNotExists:boolean):XMLNode|undefined {
	return undefined;
}

function accessFieldValue(createIfNotExists:boolean):XMLNode|undefined {
	let cur = accessCatalogEntry(createIfNotExists);
	if(!cur){
		return undefined;
	}
	
	// At this point, cur is XMLNode
	
	for(let i = 0; i < 5; ++i){
		// But here, cur is XMLNode|undefined
		let tmp = accessArray(cur, createIfNotExists);

		// Declaring the type of tmp to be XMLNode|undefined, fixes this issue
		
		if(!tmp){
			return undefined;
		}
		
		cur = tmp; // This line is what's confusing the compiler
	}
	
	return cur;
}
```

### 🙁 Actual behavior

The compiler forgets that `cur` can only be `XMLNode` inside the loop. It prints an error on the line with the call to accessArray since it thinks `cur` might be undefined

### 🙂 Expected behavior

It is provably that its value will always be `XMLNode`, since once the loop is entered, `cur` is `XMLNode`, and the only assignment to it comes from a variable that is narrowed to also be `XMLNode`.

To be frank, the logic seems a bit circular and hard to analyze from the compiler perspective. Removing the `cur = tmp;` is enough to remove the type error, for example.

Changing the code to this is also enough to let the compiler type check properly:
```ts

function accessFieldValue_workaround1(createIfNotExists:boolean):XMLNode|undefined {
	let cur_ = accessCatalogEntry(createIfNotExists);
	if(!cur_){
		return undefined;
	}
	
	// At this point, cur_ is XMLNode
	// This line will enforce the typecheck to know the type is XMLNode inside the loop too
	let cur = cur_;
	
	for(let i = 0; i < 5; ++i){
		let tmp = accessArray(cur, createIfNotExists);
		
		if(!tmp){
			return undefined;
		}
		
		cur = tmp;
	}
	
	return cur;
}
```