title: Can I make a key-value dictionary in TypeScript that's typesafe for every key-value pair?
body: <p>This question's kind of involved, and possibly best approached by exploring a rudimentary state system, so walk with me here for a minute. Suppose I have this state class:</p>
  <pre><code>class AccountState {
    public id: string;
    public displayName: string;
    public score: number;
  }
  </code></pre>
  <p>From <a href="https://stackoverflow.com/a/49286056/254830">jcalz's work here</a>, I know I can build a function that references any AccountState property in a typesafe way—I can take a property name and value, and impose the property's own type restriction on that value using generics, which is pretty impressive:</p>
  <pre><code>class Store {
    state = new AccountState();
  
    mutate&lt;K extends keyof AccountState&gt;(property: K, value: AccountState[K]): void {
      this.state[property] = value;
    }
  }
  
  const store = new Store();
  store.mutate('displayName', 'Joseph Joestar'); // ok
  store.mutate('displayName', 5); // not ok: surfaces the below typescript error
  // ts(2345) Argument of type 'number' is not assignable to parameter of type 'string'.
  </code></pre>
  <p>Using the <code>ValueOf&lt;T&gt;</code> in jcalz's answer, I can also model a sort-of-typesafe key-value dictionary. It'd probably be easiest for me to show you how it works, as well as its shortcomings, in action:</p>
  <pre><code>type ValueOf&lt;T&gt; = T[keyof T];
  
  class Store {
    state = new AccountState();
  
    mutateMany(updates: { [key in keyof AccountState]?: ValueOf&lt;AccountState&gt; }): void {
      Object.keys(updates).forEach(property =&gt; {
        const value = updates[property];
        (this.state[property] as any) = value;
      });
    }
  }
  
  const store = new Store();
  store.mutateMany({ displayName: 'Joseph Joestar', score: 5 }); // ok
  store.mutateMany({ displayName: 1000, score: 'oh no' }); // unfortunately, also ok
  store.mutateMany({ score: true }); // not ok, surfaces the below error
  // ts(2322) Type 'boolean' is not assignable to type 'ValueOf&lt;AccountState&gt;'.
  // (if AccountState had a boolean property, this would be allowed)
  </code></pre>
  <p>That second <code>mutateMany()</code> is an issue. As you can see, I can require that the key is some property of AccountState. I can also require that the value corresponds to some property on AccountState, so it has to be <code>string | number</code>. However, there is no requirement that the value corresponds to the property's actual type.</p>
  <p>How can I make the dictionary fully typesafe, so that e.g. <code>{ displayName: 'a', score: 1 }</code> is allowed but <code>{ displayName: 2, score: 'b' }</code> is not?</p>
  <p>I've considered declaring an AccountStateProperties interface which simply repeats all those properties and their values, then defining <code>mutateMany(updates: AccountStateProperties)</code>, but that would add up to a lot of code duplication for more involved state objects. I didn't know I could do some of these things until today, and I'm wondering if the typing system has something I can leverage here to make this dictionary fully typesafe without that approach.</p>