Stack Overflow Asked by andyroo on February 14, 2021
I trying to create a reusable <Column />
component that displays a list of items which each dispatch a generic payload specified by the caller when clicked.
My column takes an onItemClick
prop which is a function that dispatches a payload (a Redux action in my actual code). I want my function to be able to accept and dispatch a generic <PayloadType>
:
type ColumnProps<PayloadType> = {
menuItems: { name: string; id: number }[];
onItemClick: (payload: PayloadType) => void;
};
const Column = <PayloadType extends {}>(
props: React.PropsWithChildren<ColumnProps<PayloadType>>
) => {
const { menuItems, onItemClick } = props;
const handleButtonClick = (menuItem: MenuItem) => {
onItemClick({ info: menuItem.name });
/*
Argument of type '{ info: string; }' is not assignable to parameter of type
'PayloadType'.
'{ info: string; }' is assignable to the constraint of type 'PayloadType', but
'PayloadType' could be instantiated with a different subtype of constraint '{}'
*/
};
return (
<>
{menuItems.map((menuItem, index) => (
<button key={index} onClick={(event) => handleButtonClick(menuItem)}>
{menuItem.name}
</button>
))}
</>
);
};
Using the component:
type MenuItem = {
name: string;
id: number;
};
const testMenuItems: MenuItem[] = [
{ name: "Vanilla", id: 0 },
{ name: "Strawberry", id: 1 },
{ name: "Chocolate", id: 2 },
{ name: "Cookies & Cream", id: 3 }
];
type ColumnPayload = {
info: string;
};
export default function App() {
const columnClickHandler = (payload: ColumnPayload) => {
console.log(`clicked: ${payload.info}`);
};
return (
<div className="App">
<Column<ColumnPayload>
menuItems={testMenuItems}
onItemClick={columnClickHandler}
/>
</div>
);
}
As seen above, I’m receiving the error:
Argument of type '{ info: string; }' is not assignable to parameter of type 'PayloadType'.
'{ info: string; }' is assignable to the constraint of type 'PayloadType', but 'PayloadType' could be instantiated with a different subtype of constraint '{}'.
How can I accept and dispatch a generic payload from my component? I’m fairly new to TypeScript so I’m not sure if I’m missing something or simply approaching the problem completely wrong.
Sandbox: https://codesandbox.io/s/sweet-kalam-tt5u5?file=/src/App.tsx
The issue here is Column
cannot create a specific type without help, its only able to be aware that there is some unknown type.
That being said, one way you can achieve your generic callback is simply union the payload type with the menu item.
type MenuItemWithPayload<TPayload> = MenuItem & Payload
Then dispatch the callback with the entire menuitem. I provided some example code, notice how that ColumnMenuItem
is both payload and menuitem type? this allows type inferencing were you no longer need to define the payload type when using the column component.
https://codesandbox.io/s/eager-framework-pu4qe?file=/src/App.tsx.
A cleaner alternative might be to allow the menu item to contain a payload field. Which is similar to the union type but uses composition.
type MenuItem<TPayload = unknown> = { name: string; id: number; payload: TPayload }
https://codesandbox.io/s/friendly-currying-g1ynk?file=/src/App.tsx
Finally you can simply forward the menu item in the callback and let the parent component generate the payload it needs.
https://codesandbox.io/s/hardcore-surf-ihz96?file=/src/App.tsx
Answered by Brenden on February 14, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP