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