Reputation: 4617
I would like to create a static function that can be referenced in the child classes. An example of BaseClass
is as below
export abstract class BaseEntity {
public static of<T>(params: Partial<T>, type: new () => T): T {
const item = new type();
Object.assign(type, params);
return item;
}
}
then I extended the BaseEntity with User class like below
export class User extends BaseEntity{
public static of(params: Partial<User>): User {
return super.of<User>(params, User);
}
}
It seems like a legit code for me as I am providing User as the generic type and I have explicitly coded that the BaseEntity static function should return that of same type (which throws no error). However, writing this code produces below error
TS2417: Class static side 'typeof User' incorrectly extends base class static side 'typeof BaseEntity'. The types returned by 'of(...)' are incompatible between these types.Type 'User' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'User'.
It seems to be saying that type User cannot be returned as the returned type T from parent class is not compatible with User type. It makes no sense to me whatsoever because I am explicitly providing to the generic type that T should be User. What am I doing wrong and how can I overcome this problem?
Upvotes: 0
Views: 674
Reputation: 3905
Hmmm. Are you trying to override static methods here? That's not gonna work, I'm afraid. ;) Static methods are bound to a class' constructor instead of its prototype. You can only call them on the class itself, not on an instance of a class. So your BaseEntity.of()
method and User.of()
method are actually two completely unrelated functions.
Your BaseEntity.of()
method seems to be a factory method for your entities. Perhaps you can consider to use the BaseEntity.of()
method and drop the User.of()
method. The sample code below only creates only the static of()
method in the BaseEntity
class and uses it for initializing two User
variables. (For demonstration purposes, I also added a name
property to the User
class.)
Some additional remarks though:
of()
function: first the type
parameter (which should always be supplied), and second the params
parameter (which could be optional in case you'd want to create a default instance).BaseEntity.of()
method you assign the contents of the params
object to type
, but I assume you want to assign them to item
instead.BaseEntity.ts
:
export abstract class BaseEntity {
static of<T extends BaseEntity>(type: new () => T, params?: Partial<T>) {
const item = new type();
Object.assign(item, params);
return item;
}
//...
}
User.ts
:
import { BaseEntity } from 'BaseEntity';
export class User extends BaseEntity {
name?: string;
}
main.ts
:
import { BaseEntity } from 'BaseEntity';
import { User } from 'User';
const user1 = BaseEntity.of(User, { name: 'John Doe' });
const user2 = BaseEntity.of(User);
console.log(user1.name); // John Doe
console.log(user2.name); // undefined
I assume that your BaseEntity
class contains additional (base entity related) logic. But if that's not the case, you might want to extract the entity creation logic from the BaseEntity
class and put it in a "global" factory function instead. When doing so, you can drop the BaseEntity
class completely. As a result, your concrete entity classes will have one less dependency.
createEntity.ts
:
export function createEntity<T>(type: new() => T, params?: Partial<T>) {
const item = new type();
Object.assign(item, params);
return item;
}
User.ts
:
export class User {
name?: string;
}
main.ts
:
import { createEntity } from 'createEntity';
import { User } from 'User';
const user1 = createEntity(User, { name: 'John Doe' });
const user2 = createEntity(User);
console.log(user1.name); // John Doe
console.log(user2.name); // undefined
Upvotes: 1