score:2

Accepted answer

The issue is not in using a callback to update the state - that's fine. The issue is that when you set a property of an object using a dynamic key, typescript doesn't know what the value of the key is for any given iteration of the loop, let alone what type its value should be. So you need to tell it. You need to dynamically type your keys using generics. There's a great article about it right here.

  function updateValue<T extends keyof MyState, K extends MyState[T]>(
    name: T,
    value: K
  ) {
    setMyState(prev => ({ ...prev, [name]: value }));
  }

  React.useEffect(() => {
    keys.forEach((key) => {
      updateValue(key, "lol");
    });
  }, []);

Here is a working codesandbox

Working Typescript playground

Gives the errors exactly as it should.

Further musings:

There are some interesting quirks about this. What if you set the value to one of the possible values on your interface?

keys.forEach((key) => {
 updateValue(key, 4);  // <---- no error, but there should be
});

In theory, this should also throw an error, because key could be a, in which case the types dont match. TypeScript doesn't throw an error. However, if you specify to run this only when key === 'a', the error does occur:

keys.forEach((key) => {
  if (key === 'a'){
    updateValue(key, 4); // <---- typechecking error occurs
  }
});

In the latter case, typescript is smart enough to know that if the key is a, the type has to be a number. But in the former case, where you are trying to assign a value that matches one of the types on the interface, but not all of them, we may be seeing a bug in, or reaching the limits of typescript.


Related Query

More Query from same tag