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

TypeScript (react) : Dynamic update of object with spread operator

I have a custom type set

export type Menu =  {
    search: boolean,
    browse: boolean,
}

IM using zustand to hold state like so

interface AppState {
    menu: Menu;
    setMenu: (newMenu:Menu) => void
}
export const useStore = create<AppState>((set: SetState<AppState>, get: GetState<AppState>) => ({
    menu: {search: false, browse: false},
    setMenu: (newMenu:Menu):void => {
        set({menu: newMenu})
    }
}))

now in component i want to update this state onClick

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

const handleClick = (key: string) => {
        return (event: React.MouseEvent) => {
            setMenu({...menu, [key]: !menu[key]})
        }
    }


<div className="main-button menu-button" onClick={handleClick("search")}>

Logic is to simply toggle appropriate key: value pair to true or false. TypeScript throws error at

setMenu({...menu, [key]: !menu[key]})

I understand it wont allow because it cant guarantee menu[key] exists. If I hardcode / write menu[‘search’] it works.
What is the best course of action in this case to make it work?

>Solution :

The problem is that key is of type string, and as you said, may be a string with a name that’s not valid in the interface.

If you’re always supplying these names as hardcoded values as in your example, you an make it keyof Menu instead (which also has the convenient effect of TypeScript telling you when you call handleClick with an invalid value):

const handleClick = (key: keyof Menu) => {
// −−−−−−−−−−−−−−−−−−−−−−−^

If you have to allow string for key in handleClick, then you could use either a type guard function or a type assertion function. Then handleClick would use it, either in an if (the type guard) or just inline (the type assertion).

Type guard:

function isValidMenuKey(key: string): key is keyof Menu {
    return key === "search" || key === "browse";
}
// ...
const handleClick = (key: string) => {
    if (isValidMenuKey(key)) {
        return (event: React.MouseEvent) => {
            setMenu({...menu, [key]: !menu[key]});
        };
    } else {
        // Do what? (See type assertion version below)
    }
};

Type assertion:

// Or type assertion:
function assertIsValidMenuKey(key: string): asserts key is keyof Menu {
    if (key !== "search" || key !== "browse") {
        throw new Error(`Invalid key for Menu: "${key}"`);
    }
}
// ...
const handleClick = (key: string) => {
    assertIsValidMenuKey(key);
    return (event: React.MouseEvent) => {
        setMenu({...menu, [key]: !menu[key]});
    };
};
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