I have a tree-like data structure. I want to wrap each of the tree nodes with a node-wrapper, which adds additional functionalities to a node. But the same time I want to keep the typings intact. Can anybody help me out with that?
Here is a example showing what I want to achieve
const a = { children: [], value: 12 } as Node<number>;
const b = { children: [], value: 'string' } as Node<string>;
const c = { children: [b, a], value: true } as Node<boolean>;
const root = { children: [c]; value: null } as Node<null>;
// wrap node data with some functions, but keep typings stable
const wrappedNode = wrapNode(root);
wrappedNode.value; // type should be => null
wrappedNode.children[0].value; // type should be => boolean
wrappedNode.children[0].children[0].value; // type should be => string
wrappedNode.children[0].children[1].value; // type should be => number
My current Approach looks like:
interface Node<T, C extends Node<unknown, any[]>[]> {
children: [...C];
value: T;
}
interface WrapNode<T, C extends Node<unknown, any[]>[]> {
children: WrapNode<any, [...C]>[];
value: T;
computeValue(): any;
}
function createNode<T, C extends Node<unknown, any[]>[]>(value: T, children: [...C]): Node<T, [...C]> {
return {
value,
children,
};
}
export function wrapNode<T, C extends Node<any, any>[]>(node: Node<T, C>): WrapNode<T, typeof node.children> {
const value = node.value;
return {
...node,
computeValue: () => value,
children: node.children.map(child => wrapNode(child)),
// ^^^^^ ^^^^^^^^^^^^^^^
// types are: C[number] wrapNode<any, any>(n: Node<any, any>)
};
}
const a = createNode(12, []);
const b = createNode('str', []);
const c = createNode(null, [b, a]);
const x = wrapNode(c);
x.value; // gives me type null, ok!
x.children[0].value; // gives me any :(
x.children[1].value; // gives me any too :(
Is it even possible, with TypeScript? I'm using TypeScript 4.0.2, if that helps. Thanks in advance :)