title: How to define a Typescript function that swaps values of two object properties by name, with check of type compatibility?
body: <p>I'm trying to define a function that swaps values of two properties on an object given their names, but I would like compiler to check types compatibility (or at least check both properties to have same type):</p>
  
  <pre class="lang-js prettyprint-override"><code>function swap&lt;T, TKey1 extends keyof T, TKey2 extends keyof T&gt;(obj: T, key1: TKey1, key2: TKey2): void{
      let temp = obj[key1];
      obj[key1] = obj[key2]; 
      obj[key2] = temp;
  }
  
  
  let obj = {
      a: 1,
      b: 2,
      c: ""
  }
  
  swap(obj, "a", "b");    // good, both are numbers
  swap(obj, "a", "c");    // should not compile, swapping number with string
  </code></pre>
  
  <p><a href="https://www.typescriptlang.org/play/index.html#src=function%20swap%3CT%2C%20TKey1%20extends%20keyof%20T%2C%20TKey2%20extends%20keyof%20T%3E(obj%3A%20T%2C%20key1%3A%20TKey1%2C%20key2%3A%20TKey2)%3A%20void%7B%0D%0A%20%20%20%20let%20temp%20%3D%20obj%5Bkey1%5D%3B%0D%0A%20%20%20%20obj%5Bkey1%5D%20%3D%20obj%5Bkey2%5D%3B%20%0D%0A%20%20%20%20obj%5Bkey2%5D%20%3D%20temp%3B%0D%0A%7D%0D%0A%0D%0A%0D%0Alet%20obj%20%3D%20%7B%0D%0A%20%20%20%20a%3A%201%2C%0D%0A%20%20%20%20b%3A%202%2C%0D%0A%20%20%20%20c%3A%20%22%22%0D%0A%7D%0D%0A%0D%0Aswap(obj%2C%20%22a%22%2C%20%22b%22)%3B%20%20%20%20%2F%2F%20goood%2C%20both%20are%20numbers%0D%0Aswap(obj%2C%20%22a%22%2C%20%22c%22)%3B%20%20%20%20%2F%2F%20should%20not%20compile%2C%20swapping%20number%20with%20string" rel="nofollow noreferrer">TS playground</a></p>
  
  <p>I got some result with the following, but it requires obj to be passed twice.</p>
  
  <pre class="lang-js prettyprint-override"><code>function swap&lt;T,
      TKey1 extends keyof T,
      TKey2 extends keyof T,
      TIn extends { [p in TKey1|TKey2]: T[TKey1] } &gt;(_:T, obj: TIn, key1: TKey1, key2: TKey2): void{
      let temp = &lt;any&gt;obj[key1];
      obj[key1] = &lt;any&gt;obj[key2]; 
      obj[key2] = temp;
  }
  
  
  let obj = {
      a: 1,
      b: 2,
      c: ""
  }
  
  swap(obj, obj, "a", "b");    // good, both are numbers
  swap(obj, obj, "a", "c");    // error, as expected
  </code></pre>
  
  <p><a href="https://www.typescriptlang.org/play/index.html#src=function%20swap%3CT%2C%0D%0A%20%20%20%20TKey1%20extends%20keyof%20T%2C%0D%0A%20%20%20%20TKey2%20extends%20keyof%20T%2C%0D%0A%20%20%20%20TIn%20extends%20%7B%20%5Bp%20in%20TKey1%7CTKey2%5D%3A%20T%5BTKey1%5D%20%7D%20%3E(_%3AT%2C%20obj%3A%20TIn%2C%20key1%3A%20TKey1%2C%20key2%3A%20TKey2)%3A%20void%7B%0D%0A%20%20%20%20let%20temp%20%3D%20%3Cany%3Eobj%5Bkey1%5D%3B%0D%0A%20%20%20%20obj%5Bkey1%5D%20%3D%20%3Cany%3Eobj%5Bkey2%5D%3B%20%0D%0A%20%20%20%20obj%5Bkey2%5D%20%3D%20temp%3B%0D%0A%7D%0D%0A%0D%0A%0D%0Alet%20obj%20%3D%20%7B%0D%0A%20%20%20%20a%3A%201%2C%0D%0A%20%20%20%20b%3A%202%2C%0D%0A%20%20%20%20c%3A%20%22%22%0D%0A%7D%0D%0A%0D%0Aswap(obj%2C%20obj%2C%20%22a%22%2C%20%22b%22)%3B%20%20%20%20%2F%2F%20good%2C%20both%20are%20numbers%0D%0Aswap(obj%2C%20obj%2C%20%22a%22%2C%20%22c%22)%3B%20%20%20%20%2F%2F%20error%2C%20as%20expected" rel="nofollow noreferrer">TS playground</a></p>
  
  <p>Alternatively, I can achieve desired result with conditional types if I return a function, but it is too easy to forget second call.</p>
  
  <pre class="lang-js prettyprint-override"><code>function swap&lt;T,
      TKey1 extends keyof T,
      TKey2 extends keyof T&gt;(obj: T, key1: TKey1, key2: TKey2):
                                              T[TKey1] extends T[TKey2] ? T[TKey2] extends T[TKey1] 
                                                  ? () =&gt; void
                                                  : never : never {
  
      return &lt;any&gt;(() =&gt; {
          let temp = &lt;any&gt;obj[key1];
          obj[key1] = &lt;any&gt;obj[key2];
          obj[key2] = temp;
      });
  }
  
  
  let obj = {
      a: 1,
      b: 2,
      c: ""
  }
  
  swap(obj, "a", "b")();    // good, both are numbers
  swap(obj, "a", "c")();    // error, as expected
  </code></pre>
  
  <p><a href="https://www.typescriptlang.org/play/index.html#src=function%20swap%3CT%2C%0D%0A%20%20%20%20TKey1%20extends%20keyof%20T%2C%0D%0A%20%20%20%20TKey2%20extends%20keyof%20T%3E(obj%3A%20T%2C%20key1%3A%20TKey1%2C%20key2%3A%20TKey2)%3A%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20T%5BTKey1%5D%20extends%20T%5BTKey2%5D%20%3F%20T%5BTKey2%5D%20extends%20T%5BTKey1%5D%20%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3F%20()%20%3D%3E%20void%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3A%20never%20%3A%20never%20%7B%0D%0A%0D%0A%20%20%20%20return%20%3Cany%3E(()%20%3D%3E%20%7B%0D%0A%20%20%20%20%20%20%20%20let%20temp%20%3D%20%3Cany%3Eobj%5Bkey1%5D%3B%0D%0A%20%20%20%20%20%20%20%20obj%5Bkey1%5D%20%3D%20%3Cany%3Eobj%5Bkey2%5D%3B%0D%0A%20%20%20%20%20%20%20%20obj%5Bkey2%5D%20%3D%20temp%3B%0D%0A%20%20%20%20%7D)%3B%0D%0A%7D%0D%0A%0D%0A%0D%0Alet%20obj%20%3D%20%7B%0D%0A%20%20%20%20a%3A%201%2C%0D%0A%20%20%20%20b%3A%202%2C%0D%0A%20%20%20%20c%3A%20%22%22%0D%0A%7D%0D%0A%0D%0Aswap(obj%2C%20%22a%22%2C%20%22b%22)()%3B%20%20%20%20%2F%2F%20good%2C%20both%20are%20numbers%0D%0Aswap(obj%2C%20%22a%22%2C%20%22c%22)()%3B%20%20%20%20%2F%2F%20error%2C%20as%20expected" rel="nofollow noreferrer">TS playground</a></p>
  
  <p>Is it possible to simplify above examples? Can I maybe supply some type instead of <code>never</code> that will indicate error to type system? </p>
  
  <p>P.S. I know about <code>[obj.a, obj.b] = [obj.b, obj.a];</code> , but would like to avoid it.</p>