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
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?
CodePudding user response:
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]});
};
};