Consider this generic tree structure:
export interface TreeItem {
subItems?: TreeItem[];
}
The nodes are polymorphic. In effect, this is what I put in the tree :
export class MenuItemTypeA implements MenuItem {
public someFieldExclusiveToTypeA: string;
public subItems: MenuItem[];
}
export class MenuItemTypeB implements MenuItem {
public someFieldExclusiveToTypeB: string;
public subItems: MenuItem[];
}
Example of tree
const tree = [
new MenuItemTypeA("a1", [
new MenuItemTypeB("b2", []),
new MenuItemTypeA("a2", [])
]),
new MenuItemTypeB("b1", [])
]
Now this the generic method that lets me filter the tree, by passing a predicate method (filter) which decides if the node stays in the tree or not :
public static filterTree<T extends TreeItem>(nodes: T[], filter: (node: T) => boolean): T[] {
const updatedItems = [];
for (const node of nodes) {
if (filter(node)) {
updatedItems.push(node);
}
node.subItems = this.filterTree(node.subItems ?? [], filter);
}
return updatedItems;
}
For example :
const filteredTree = filterTree(tree, (_) => true); // Leaves all the nodes
Problem :
Typescript is not happy with this line :
node.subItems = this.filterTree(node.subItems ?? [], filter);
Error:
Argument of type TreeItem[] is not assignable to parameter of type T[]
Type TreeItem is not assignable to type T
TreeItem is assignable to the constraint of type T, but T could be instantiated with a different subtype of constraint TreeItem
I’m not sure what the problem is, as I’m not re-instantiating anything, just copying the nodes’ references without changing the nodes themselves.
Is there an easy fix? More genrally, is this approach flawed? (i.e. what is a good practice for a generic tree with strongly-typed polymorphic nodes? I couldn’t figure it out with only interfaces)