Gabriel
Gabriel

Reputation: 332

ANGULAR: How to extend an abstract class two levels with generics

I am trying to extend a class with two levels of inheritance with generics, like the following diagram:

enter image description here

First attempt, I declared BaseResourceTableComponent as extending BaseResourceListComponent<T extends BaseResourceModel>:

export abstract class BaseResourceModel {
    public id?: number;
}

export abstract class BaseResourceListComponent<T extends BaseResourceModel> {
  public resources: T[] = [];
  ...
}

export abstract class BaseResourceTableComponent extends BaseResourceListComponent<T extends BaseResourceModel>  { // <--- ERROR!
  dataSource: MatTableDataSource<T>;
  ...
}

// now, the non abstract classes

export class User extends BaseResourceModel {
  constructor(
    public name?: string,
  ) {
    super();
  }

export class UserTableComponent extends BaseResourceTableComponent<User> {
  ...
}

I get the following error:

error TS2304: Cannot find name 'T'.

Second attempt, I declared BaseResourceTableComponent as extending BaseResourceListComponent<BaseResourceModel> (without the T extents):

export abstract class BaseResourceModel {
    public id?: number;
}

export abstract class BaseResourceListComponent<T extends BaseResourceModel> {
  public resources: T[] = [];
  ...
}

export abstract class BaseResourceTableComponent extends BaseResourceListComponent<BaseResourceModel>  {
  dataSource: MatTableDataSource<BaseResourceModel>;
  ...
}

// now, the non abstract classes

export class User extends BaseResourceModel {
  constructor(
    public name?: string,
  ) {
    super();
  }

export class UserTableComponent extends BaseResourceTableComponent<User> { // <--- ERROR!
  ...
}

It passes, no error in the BaseResourceTableComponent declaration but I get the following error further on UserTableComponent declaration:

error TS2315: Type 'BaseResourceTableComponent' is not generic.

I am really confused here!

Am I missing something? Workarounds?

Upvotes: 0

Views: 1676

Answers (2)

Andrei Gătej
Andrei Gătej

Reputation: 11934

export abstract class BaseResourceListComponent<T extends BaseResourceModel> {
  public resources: T[] = [];
  ...
}

Here you define the BaseResourceListComponent class with a generic type T extends BaseResourceModel. The extends BaseResourceModel part is a type constraint which imposes restrictions on the types T can have. What this means is that T must either be BaseResourceModel or one of its subtypes.
A type B is a subtype of A if B is of type A and has additional information(i.e: properties, methods).

export abstract class BaseResourceTableComponent extends BaseResourceListComponent<T extends BaseResourceModel>  { // <--- ERROR!
  dataSource: MatTableDataSource<T>;
  ...
}

Here you get an error because BaseResourceListComponent expects a type, and here you provide T extends BaseResourceModel, but you haven't defined T at all.

You could solve it with

export abstract class BaseResourceTableComponent<T extends BaseResourceModel> extends BaseResourceListComponent<T> { }

Now, T is a type, no matter if it's concrete or generic, as long as T fulfills the constraint: extends BaseResourceModel.

TypeScript Playground

Upvotes: 1

ThisIsNoZaku
ThisIsNoZaku

Reputation: 2443

Both of your problems are caused by BaseResourceTableComponent not being declared generic.

export abstract class BaseResourceTableComponent extends BaseResourceListComponent<T extends BaseResourceModel> says that the generic parameter for BaseResourceListComponent is T, but doesn't give anyway to determine what T is because BaseResourceTableComponent neither specifies a concrete parameter nor gives a way to declare a generic parameter that can be essentially passed down to the parent class.

Extending a generic class does not make the class doing the extending generic; In fact, you're trying to rely on exactly that with export class UserTableComponent extends BaseResourceTableComponent<User>.

Upvotes: 0

Related Questions