Robin_f
Robin_f

Reputation: 465

Filter on generic types

I'm trying to write a function using TypeScript that will allow me to filter a list of objects depending on their type. The result should be a function that will allow me to do either:

filter<Foo>(items);

or

filter(items, Foo);

I've been trying to do it in the following way:

class Foo {
    public constructor(public name: string, public type: string) {

    }
}

class Bar extends Foo { }

const items: Foo[] = [
    new Foo('Foo', 'A'),
    new Bar('bar', 'A'),
    new Foo('baz', 'B'),
];

const filter = <T extends Foo>(items: any[], typeT: T): T[] => {
    return items.filter(item => item instanceof typeT)
};

console.log(filter(items, Foo));

But this doesn't work.

How can I make this work?

TypeScript example

Upvotes: 6

Views: 10765

Answers (2)

adrice727
adrice727

Reputation: 1492

You can simplify things by using the name property of the class:

class Foo {
    public constructor(public name: string, public type: string) {

    }
}

class Bar extends Foo { }

const items: Foo[] = [
    new Foo('Foo', 'A'),
    new Bar('bar', 'A'),
    new Foo('baz', 'B'),
];

const filter = (items: Foo[], typeT: Function): Foo[] => {
    return items.filter(item => item.constructor.name === typeT.name)
};

console.log(filter(items, Foo)); // Array [ {…}, {…} ]

Upvotes: 0

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250406

When you pass in the type you are actually passing in the constructor of the class. Your signature is passing in an instance of T. You should try:

const filter = <T extends Foo>(items: any[], typeT: new (...params : any[]) => T): T[] => {
    return items.filter(item => item instanceof typeT)
};

Note: In your example all the items in the array will pass the filter, because Bar is derived from Foo and thus is also an instance of Foo. If you want only object of type Foo and not derived, you could use item.constructor == typeT

Upvotes: 5

Related Questions