Home > Software design >  typescript generics class, method parameter and method return type
typescript generics class, method parameter and method return type

Time:05-06

I have this function

public getCollection<T>(collectionType: T): Collection<T> {
  return new Collection<T>("some-arg1", "some-arg2")
}

and in Collection class I have this

export class Collection<T> {
  public add (T item) {
    // .. logic
  }
}

I have a user class defined like this

export class Student {

}

and when I attempt to do

getCollection(Student).add(new Student());

There is an error

TS2345: Argument of type 'Student' is not assignable to parameter of type 'typeof Student'.   Property 'prototype' is missing in type 'Student' but required in type 'typeof Student'.

Following works fine.

new Collection<Student>().add( new Student());

So what is wrong when the function returns generic collection?

CodePudding user response:

T is actually of type typeof Student. Student is an instance of the class, while typeof Student is the constructor. To get the instance type of a constructor, use the intuitively named InstanceType built-in:

public getCollection<T>(collectionType: T): Collection<InstanceType<T>> {
  return new Collection<InstanceType<T>>("some-arg1", "some-arg2")
}

But now you have to add a constraint which shouldn't be too much of a problem:

public getCollection<T extends new (...args: any[]) => any>(...

This should result in:

public getCollection<T extends new (...args: any[]) => any>(collectionType: T): Collection<InstanceType<T>> {
  return new Collection<InstanceType<T>>("some-arg1", "some-arg2")
}

CodePudding user response:

This error is caused due to the fact that the generic type is inferred from the parameter; meaning that T is not Student but it is actually typeof Student. Hence return new Collection<T> does not behave like return new Collection<Student> but is instead return new Collection<typeof Student>.

This can be fixed by actually assigning a type to the generic parameter:

getCollection<Student>(Student)

The above makes the use of the parameter redundant, hence getCollection can be refactored to the following:

getCollection<T>(): Collection<T> {
  return new Collection<T>("some-arg1", "some-arg2");
}

and be called as:

getCollection<Student>()

Link to the playground.

  • Related