Repository: TypeScript
Issue: type guard on `this` inside method produces strange narrowings and seemingly inconsistent error messages
Labels: Bug

# Bug Report

<!--
  Please fill in each section completely. Thank you!
-->

### 🔎 Search Terms

`this` type guard, method, hidden circularity, annotated return type, user defined type guard

### 🕗 Version & Regression Information

- This changed between versions 3.7.5 and 3.8.3 

### ⏯ Playground Link

[Playground link with relevant code](https://www.typescriptlang.org/play?ts=4.5.4#code/MYGwhgzhAEBiD28A8AVaBTAHgF3QOwBMYJsAnASzwHNoAfaPAVwFsAjdUgPmgG8BYAFDRoweHhKlGwbPFIAKAA6NWIcsGgLS8BQC5oKAJS9oAX0GDhzdNgAW8AgFU8YPHnjYwuAnKP8hw6HIAM2g5G0gAZTJKKgAFLQU5W3IIA18LAOFSa0ZSPGhkiAA6TW0imQcFBQ4AYUh0H2gAeiaMUi1SAEJoDMyW6HjtDmwAT2gAcgqq2vrx6AJ4dBg3bAxMFNWxApHqiYkYugYWdlJx3tNz4NDwiAA5Y45BxMK03nOsnLyCmxSShPL4LByJh0N4DABuZqtDgdbrvKEDBLDMbjUoKOYLJYMdxrDbQLajXbjPDoABuHDO-mEZip3y0AHcGOhGQBRdqyHzgjI0jJWWz2ACCrncnlBPj0+2ob1pVzCkWi1CeSR+qXStI+2Fy+UKfzKU2qpDqEAaEIR8AA1mARucaZlZTd7mxHgllSlXn5Mhqtd9fmiAUCQWDIf0LVabedbAymaz2fIIdzzAIeQIgow8NJyFsblEKIqXdg9AhkJKaPQmE6uAY9KsUnBEEgS9weNBspqvoT0PAQthdQpoABeQfQABEJeHFxTaYzWcgjpOSoLdeLCtLRwrnCrBUCMCLSHLJybLc+2p2ne7vYHQ+H+4445pQA)

### 💻 Code

<!-- Please post the relevant code sample here as well-->
```ts
class Foo<T extends string | number> {
  constructor(public prop: T) { }

  method() {
    if (hasStringProp(this)) {
      return this.prop.toUpperCase() // error! 
      // Property 'toUpperCase' does not exist on type 'string | number'
    }
    if (hasNumberProp(this)) {
      return this.prop.toFixed(); // error! 
      // Property 'prop' does not exist on type 'never'
    }
    throw new Error();
  }
}

function hasStringProp(t: Foo<string | number>): t is Foo<string> { return typeof t.prop === "string" }
function hasNumberProp(t: Foo<string | number>): t is Foo<number> { return typeof t.prop === "number" }
```

### 🙁 Actual behavior

Inside the respective blocks in `method()`, `this` is seen (via quickinfo) to have been narrowed to `this & Foo<string>` and then `this & Foo<number>`, and `this.prop` is seen to have been narrowed to `T & string` and then `T & number`.  But actually trying to access `this.prop` as such produces errors about how `this.prop` is apparently `string | number` (as if no narrowing has occurred) in the first case, and how `this` is apparently `never` (as if too much narrowing has occurred) in the second case.

It really seems like the actual problem here is a hidden circularity.  The return type of `method()` depends on the return type of `hasStringProp(this)` and `hasNumberProp(this)` which depends on `Foo` which depends on the `method()`.  But no circularity warning is issued.  Instead there's just these weird narrowing errors.

If you annotate the return type of `method()` explicitly, this problem goes away (starting with TS version 4.1.0-dev.20201028):

```ts

  method(): string {
    if (hasStringProp(this)) {
      return this.prop.toUpperCase(); // okay
    }
    if (hasNumberProp(this)) {
      return this.prop.toFixed(); // okay
    }
    throw new Error();
  }
```

### 🙂 Expected behavior

I'd expect either everything should just work, or if there's a circularity issue then it should be mentioned explicitly on `method()`.  

Comes from [this Stack Overflow question](https://stackoverflow.com/q/71125334/2887218)