Lets have simple class hierarchy and the list of instances. I want to filter items from the list of instances by the class.
There are multiple issues:
- can not use
typeof T
syntax. How to write it? - can not mark
T
asout
variance - of course I do not want to instantiate anything in the method, but only return instances ofT
. How to tell it to TS?
class A {
}
class B extends A {
}
class C extends A {
}
function filterByClass<out T extends A>(cls: typeof T, list: A[]): T[]
{
return list.filter((item) => item instanceof T);
}
Edit:
The answer (from Adrian Kokot) worked until I realized that unfortunately I made the example "too simple". In reality there are constructors with parameters, more like this:
class B extends A {
constructor(pb: any) {
super();
}
}
class C extends A {
constructor(pc: any) {
super();
}
}
Using modified playground from the anwer, the problem with variance emerges:
const a = new A();
const b = new B(1);
const c = new C(1);
const arrC = filterByClass(C, arr);
Argument of type 'typeof C' is not assignable to parameter of type 'new () => C'.
Types of construct signatures are incompatible.
Type 'new (pc: any) => C' is not assignable to type 'new () => C'.
CodePudding user response:
You can specify the first argument as a constructor, so you will be able to pass the class name.
You can use
is
operator as a return type in filter method, to tell typescript, that item is instanceof the passed class.
Possible solution:
function filterByClass<T extends A>(cls: new () => T, list: A[]): T[]
{
return list.filter((item): item is T => item instanceof cls);
}
filterByClass(C, [new A(), new B(), new C()]); // returns C[];
Answer to edit:
To use classes that have different constructors, you can just add ...args: any[]
to the first argument:
function filterByClass<T extends A>(cls: new (...args: any[]) => T, list: A[]): T[]
{
return list.filter((item): item is T => item instanceof cls);
}