I need to render a list of products from the data stored in an object with a react router v6. I typed the object and created the typed function to get the value of a key following what is taught in this github answer. This eliminated the previous error of Argument of type 'string' is not assignable to parameter of type 'never' ts(2345), but a new error appeared: Argument of type 'string' is not assignable to parameter of type ' never' ts(2345), which is pointed to the slug parameter of the getKeyValue call in the constant in the line const shoe = getKeyValue(slug)(shoes);
.
const shoes: { [key: string]: { name: string; img: string } } = {
'air-jordan-3-valor-blue': {
name: 'VALOUR BLUE',
img: 'https://secure-images.nike.com/is/image/DotCom/CT8532_104_A_PREM?$SNKRS_COVER_WD$&align=0,1',
},
'jordan-mars-270-london': {
name: 'JORDAN MARS 270 LONDON',
img: 'https://secure-images.nike.com/is/image/DotCom/CV3042_001_A_PREM?$SNKRS_COVER_WD$&align=0,1',
},
'air-jordan-1-zoom-racer-blue': {
name: 'RACER BLUE',
img: 'https://secure-images.nike.com/is/image/DotCom/CK6637_104_A_PREM?$SNKRS_COVER_WD$&align=0,1',
},
};
const getKeyValue =
<T extends object, U extends keyof T>(key: U) =>
(obj: T): T[U] =>
obj[key];
function LaunchIndex() {
return (
<ul>
{Object.entries(shoes).map(([slug, { name, img }]) => (
<li key={slug}>
<Link to={`/launch/products/${slug}`}>
<h2>{name}</h2>
<img src={img} alt={name} />
</Link>
</li>
))}
</ul>
);
}
function LaunchShoe() {
const { slug } = useParams();
const shoe = getKeyValue(slug)(shoes); // error here
if (!shoe) {
return <h2>Not found!</h2>;
}
const { name, img } = shoe;
return (
<div>
<h2>{name}</h2>
<img src={img} alt={name} />
</div>
);
}
To resolve this error I followed what is in this github answer, I changed the line to const shoe = getKeyValue(slug as never)(shoes);
. So it worked, the application worked as expected, but I can't understand what's happening...
CodePudding user response:
Your implementation of getKeyValue
is different from the Github post you linked. Here's the linked version:
const getKeyValue = <T extends object, U extends keyof T>(obj: T) => (key: U) =>
obj[key];
and here's your version:
const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T): T[U] =>
obj[key];
The problem is that you reversed the arguments. When you pass in the key parameter first, Typescript checks the constraint U extends keyof T
and has no idea what to do because it has no idea what T
is... the object hasn't been passed in yet! The result is the error message you saw.
If you reverse the arguments to match the implementation from the Github post, your code should work correctly.