Skip to content
Advertisement

How to access object property with dynamic key in Typescript?

In my component Product.tsx I receive an object via API call that could look like this:

{
  title: "Product 1",
  status: "active",
  tax: true,
  variants: [
    {
      id: 1,
      sku: 'PRD1',
      price: 24.00,
      price_reduced: 19.95,
      size: 'M'
    },
    {
      id: 2,
      sku: 'PRD2',
      price: 24.00,
      price_reduced: 19.95
      size: 'L'
    },
  ]
}

I then render each variant as a row in a table where each column displays the price, size etc. as editable input fields. onChange of each input triggers updateVariant(variant.id, key, e); where key is one of the keys of a variant and e is the input event.

updateVariant should update the value of a variant with the given key and looks like this:

  const updateVariant = (
    id: number,
    key: string,
    e: React.FormEvent<HTMLInputElement>
  ) => {
    setProduct((prevState) => {
      const update = { ...prevState };
      const i = update.variants.findIndex((variant) => variant.id === id);
      const updatedVariant = update.variants[i];
      updatedVariant[key] = e.currentTarget.value;   // errors on this line
      update.variants[i] = udpatedVariant;
      return update;
    });
  };

It gives me an error on updatedVariant[key] = e.currentTarget.value;:

Element implicitly has 'any' type because expression of type 'string | number' can't be
used to index type '{id: number, sku: string, ...}'. No index signature with a parameter
of type 'string' was found on type '{id: number, sku: string ...}'

I tried changing it to: updatedVariant[key as keyof typeof updatedVariant] = e.currentTarget.value; which gives me the error type 'any' is not assignable to type 'never'

I am quite new to Typescript and completely lost how to make this work.

EDIT: I got it to work temporarily with

updatedVariant[key as keyof typeof updatedVariant] = e.currentTarget.value as never;

But this can’t be the right / clean solution to this?

Advertisement

Answer

I got it working by declaring an interface like so:

interface Variant {
  [key: string]: string | number | string[];
}

and then

(updatedVariant as Variant)[key] = e.currentTarget.value;

My mistake was to think that key was missing a type or something when in fact I needed an interface for updatedVariant.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement