Is it possible to dynamically determine the type by the name of the object's proxy key in TypeScript

I am writing a wrapper class that will contain functions for interacting with the user entity, such methods as checking that the user is a guest, functions for checking his permissions, etc. and one of the methods I want to make an accessor to its fields like name, email, id. And I had the idea that the accessor would return me data from the user himself by the name of the field in the entity. Here is the code I have and the comments to it that I want to change:

export interface UserModel {
  id: number
  name: string
  email: string
}

export class User {
  constructor(private _user: UserModel | null) {
  }

  // I can return the type I need through the generic, but I would like to be able to analyze the key that is in the UserModel model using the name parameter
  public get<T>(
    // This works great for me, autocomplete only lets me enter id,name,email
    name: keyof UserModel
  ): T {
    if (this._user === null) {
        throw new Error("User is empty.")
    }

    // And finally, instead of the generic T, I would like something like as UserModel[name]
    return this._user[name] as T
  }

  public isGuest(): boolean {
    return this._user === null;
  }
}

Is it possible to arrange this so that when I use the get method of an instance of the User class for example:

const user = new User({...})

// The typescript told me that the string type would be returned to me instead of using the generic type get<string>('email')
user.get('email') // return type string
user.get('id') // return type number

>Solution :

You just need to capture the type of the key that is passed in using a generic type parameter. You can then use it to index into UserModel to get the actual type of the key passed in:

export class User {
  constructor(private _user: UserModel | null) {
  }

  
  public get<K extends keyof UserModel>(
    name: K
  ): UserModel[K] {
    if (this._user === null) {
        throw new Error("User is empty.")
    }
    return this._user[name]
  }

  public isGuest(): boolean {
    return this._user === null;
  }
}

Playground Link

Leave a Reply