h01001000
h01001000

Reputation: 281

Typing a function that filters an arrayTypeScript

I am trying to get items from an array filtered using a supplied constructor using TypeScript. I have not been able to solve the scenario using TypeScript. Am I close? Thanks!

type IConstructor = new (...args: any[] ) => {};
class Apple extends Thing { ... }
class Orange extends Thing { ... }
const items: Thing[] = [
    new Apple();
    new Orange();
];

let apples = getItemsOfType( Apple );  //Hopefully using the function like this

//Type 'Thing[]' is not assignable to type 'TypeOf[]'.
//Type 'Thing' is not assignable to type 'TypeOf'.
//'TypeOf' could be instantiated with an arbitrary type which could be unrelated to 'Thing'.
//=================================================================================
getItemsOfType<TypeOf extends IConstructor>( TypeConstructor: TypeOf ): TypeOf[] {
    return items.filter( item => {
        return item instanceof TypeConstructor;
    });
}

Upvotes: 0

Views: 50

Answers (1)

aleksxor
aleksxor

Reputation: 8340

Well, you were pretty close. Somewhat generic version would look like that:

function getItemsOfType<T extends IConstructor>(ctor: T): InstanceType<T>[] {
    return items.filter( (item): item is InstanceType<T> => {
        return item instanceof ctor;
    });
}

playground link

You have to explicitly annotate filter cb function as type guard to hint typescript about it's 'type-level' meaning.

Or a more generic version with stronger type constraints and no implicit dependency on free variable items inside the function:

function getItemsOfType<
    T extends unknown[], 
    U extends IConstructor<ArrayElement<T>>
>(items: T, ctor: U): InstanceType<U>[] {
    return items.filter( (item): item is InstanceType<U> => {
        return item instanceof ctor;
    });
}

playgroud link

Upvotes: 1

Related Questions