Home > Software design >  Property 'id' does not exist on type 'User' ts(2339) when using generic method f
Property 'id' does not exist on type 'User' ts(2339) when using generic method f

Time:12-28

TypeScript playground here.

Let us define an interface:

export interface User {
  id: string;
}
export class UserInstance implements User {
  id: string;


  constructor(
    id: string,
  ) {
    this.id = id;
  }
}

Then, suppose we have an abstract class:

import { Model } from "objection";

export abstract class AbstractModel<T> extends Model {
  abstract mapFromDB<T>(model: AbstractModel<T>): T;
  abstract mapToDB<T>(domain: T): AbstractModel<T>;
}

and a concrete implementation of that class:

export class UserDetailModel
  extends AbstractModel<User>
{


  /* Properties */
  id?: string;

  mapFromDB<User>(model: UserDetailModel): User {
    const user = new UserInstance(
      model.id as string,
    );

    return user as User;
  }

  mapToDB<User>(domain: User): UserDetailModel {
    const row = new UserDetailModel();
    row.id = domain.id; // this throws an error "Property 'id' does not exist on type 'User'. ts(2339)"
    return row;
  }

}

I do not understand that error. The interface User does have a property id. So why does it throw this error?

Is it because I am using generics in the wrong way? What I want to achieve in the end, is simply a conversion from my domain to my model, ready to be inserted in the DB (it is a model in the "Objection js" sense). Of course, the AbstractModel exists for all domain entity T.

I notice that the following works fine, so it really must be my use of generics that is wrong, but I do not get why:

  test(domain: User): UserDetailModel {
    const row = new UserDetailModel();
    row.id = domain.id; // no error 
    return row;
  }

It looks like this question but I do not really understand the reason.

CodePudding user response:

You have name confusion. Firstly this declaration

mapFromDB<User>(model: UserDetailModel): User 

means that you have generic method with type parameter named User. It is not your interface User. So it has no property named id.

In order to fix it. You have to fix AbstractModel declaration first. In your declaration you haven't use class type parameter T. Instead you make both class methods - generic with their own type parameter T. You need to change AbstractModel like this:

export abstract class AbstractModel<T> extends Model {
  abstract mapFromDB(model: AbstractModel<T>): T; 
  abstract mapToDB(domain: T): AbstractModel<T>;
}

Next you should get rid of User type parameter

export class UserDetailModel
  extends AbstractModel<User>
{

  /* Properties */
  id?: string;

  mapFromDB(model: UserDetailModel): User { // removed <User>
    const user = new UserInstance(
      model.id as string;
    );

    return user as User;
  }

  mapToDB(domain: User): UserDetailModel { // removed <User>
    const row = new UserDetailModel();
    row.id = domain.id;
    return row;
  }

}

now it will work as expected without exception

  • Related