zeroin
zeroin

Reputation: 6025

Typescript generics: Argument of type 'X' is not assignable to parameter of type 'T'

Here is some typescript code:

class ClassA<T extends BlueItem = BlueItem> {

    protected _list: Array<T>;

    get list(): Array<T> {
        return this._list;
    }
}

class ClassB<T extends GreenItem = GreenItem> extends ClassA<T> {
    constructor() {
        super();
        this._list.push(new GreenItem());
    }
}

class BlueItem {
    protected blueItemProperty: number;
}


class GreenItem extends BlueItem {
    protected greenItemProperty: number;
}

Compiler says "Argument of type 'GreenItem' is not assignable to parameter of type 'T'. What would be a correct approach to solve this?

Upvotes: 2

Views: 2821

Answers (3)

peter
peter

Reputation: 15089

The issue is that you can't generate the correct types (T) inside your class, because you don't know what T is. Since you don't want to pass a factory function into your class, the next best solution would be to use union types and allow both types, like this:

class ClassA<T = BlueItem> {

   protected _list:Array<T | BlueItem>;

   get list(): Array<T | BlueItem> {
    return this._list;
   }
}

class ClassB<T = GreenItem> extends ClassA<T | GreenItem> {
}

class ClassC extends ClassB<RedItem> {
}

However, here you'd need to check the items from the list to be the right type.

Upvotes: 1

Rodris
Rodris

Reputation: 2848

Cast the GreenItem to T.

class ClassB<T extends GreenItem = GreenItem> extends ClassA<T> {
    constructor() {
        super();
        this._list.push(<T>new GreenItem());
    }
}

Upvotes: 1

Amid
Amid

Reputation: 22352

A simple way to solve the problem would be to make constructor of the list items also "generic":

class ClassB<T extends GreenItem = GreenItem> extends ClassA<T>
{
  constructor(listItemCtor: {new(): T})
  {
    super();

    const t = new listItemCtor();

    this._list.push(t);
  }
}

Its not universal solution - but if applicable - it will help.

As an option you can also drop generics altogether in the base class:

class ClassA
{
    protected _list: Array<any>;
}

class ClassB<T extends GreenItem = GreenItem> extends ClassA
{
  constructor()
  {
    super();

    this._list.push(new GreenItem());
  }

  get list(): Array<T>
  {
    return this._list;
  }
}

You will not loose much as you still have type checking on public properties of ClassB. And _list is internal.

Upvotes: 2

Related Questions