Q:

Typescript type-queries in classes

I am trying to get typescript type-queries to work in classes.
Normally type-queries work like this:
const user = { name: "Patrick", age: 17 };
let typeofUser: typeof user;

In the example above the "typeofUser" would have the the type:
{ name: string, age: number }

So far so good. But I'm trying to get type-queries to work with classes.
For example:
class App {
  states = {
    menu: {
      login() {
        console.log("Menu.login");
      }
    },
    game: {
      update() {
        console.log("Game.update");
      }
    }
  };

  constructor() {
    // FAILS => Cannot find name "states"
    const typeofStates: typeof states = {};

    // FAILS => Cannot find name "states"
    const keyofStates: keyof states = "game";
  }
}

My question is: How can I access class members for type-queries,
with eighter the "typeof" or "keyof" operator?
Typescript playground sample

A:

Thanks to @cartant in the comments. I'm answering this question myself since he did not post his comment as a answer.

To type-query the type of a class member you can use several methods:

Access the prototype:
App.prototype.states

Access it via a indexer
App["states"]

A important thing to note is that this and the above method may not work when the member is private or static.

The polymorphic this type
Everything that has a this in Javascript has a "this type" in typescript. This "this" type can be queried like every other index type.
this["states"]

Note: You may have issues when using this technique when trying to call a function in another context. Also you cannot use the dot operator! You must use the object index operator instead

The this type-query is very useful in combination with class-inheritance
If you were to Programm a Statemachine for example you could have a abstract "Statemachine" Class and use the this-type-query to get the type of a property in the child class
This may look like this:
abstract class Statemachine {
  /* The typescript compiler won't actually infer "object" but 
    {
       menu: { 
         login() => void
       },
       game: {
         update() => void 
       }
    }

    > In case of the example below! 
  */
  abstract states: object

  getState<K extends keyof this["states"]>(name: K): T[K] {
    return this.states[name];
  }
}

class Game extends Statemachine {
  private states = {
     menu: {
       login() {
         console.log("Menu.login")
       }
     },
     game: {
       update() {
         console.log("Game.update") }
      }
   }
}

const game = new Game()

// Compiler error
game.getState("not a key of game.states")

// Works and intellisense for login()
game.getState("menu")

Reference: https://www.typescriptlang.org/docs/handbook/advanced-types.html
Look for polymorphic this.
I hope the code actually works Im on vacation and had no chance to check it...

