Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

In typescript how to restrict the list of properties passed to a function as a parameter?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

What is the elegant way to solve this problem ?

>Solution :

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);
    });
}

Playground link

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading