I have this:
export interface RegionNode {
nodeSelected: boolean;
nodeEditable: boolean;
zone: Partial<Zone>;
parent: RegionNode | null;
children: RegionNode[];
}
And I would like a generic function doing this:
function setNodeAndChildrenProperty(node: RegionNode, property: keyof RegionNode, state: boolean): void {
// @ts-ignore
node[property] = state;
node.children.forEach((child) => {
setNodeAndChildrenProperty(child, property, state);
});
}
But as you can see I had to use @ts-ignore, as I don't know how to restrict the list of allowed properties to "nodeSelected" and "nodeEditable" among other issues.
What is the elegant way to solve this problem ?
CodePudding user response:
You can do that by having a mapped type that will return a type for just the properties assignable to a given type (in your case, boolean
):
type PropertiesOfType<ObjType, PropType> = {
[Key in keyof ObjType as ObjType[Key] extends PropType ? Key : never]: ObjType[Key];
};
The as
clause in the [Key in ____]
omits keys (by using never
) for properties that aren't assignable to the target property type. So for instance, PropertiesOfType<RegionNode, boolean>
gives us this type:
{
nodeSelected: boolean;
nodeEditable: boolean;
}
Then you can use that in your function, so TypeScript knows that the type of node[property]
is compatible with the type of state
:
function setNodeAndChildrenProperty(
node: RegionNode,
property: keyof PropertiesOfType<RegionNode, boolean>, // <====
state: boolean
): void {
node[property] = state;
node.children.forEach((child) => {
setNodeAndChildrenProperty(child, property, state);
});
}