title: How to infer keyof Record<string, unknown[]> in TypeScript
body: <p>I have been reading "Programming TypeScript." In a chapter of the book, I faced this problem.</p>
  
  <p>I am trying to implement a wrapper of EventEmitter, which has restrictions on the event names and it's arguments. The code uses Generics for reusability.</p>
  
  <p>The code from the book:</p>
  
  <pre class="lang-js prettyprint-override"><code>import EventEmitter from 'events'
  
  class SafeEmitter&lt;
    Events extends Record&lt;PropertyKey, unknown[]&gt;
  &gt; {
    private emitter = new EventEmitter
    emit&lt;K extends keyof Events&gt;(
      channel: K,
      ...data: Events[K]
    ) {
      return this.emitter.emit(channel, ...data)
    }
    on&lt;K extends (keyof Events)&gt;(
      channel: K,
      listener: (...data: Events[K]) =&gt; void
    ) {
      return this.emitter.on(channel, listener)
    }
  }
  </code></pre>
  
  <p>The error:</p>
  
  <pre><code>(parameter) channel: K extends keyof Events
  Argument of type 'K' is not assignable to parameter of type 'string | symbol'.
    Type 'keyof Events' is not assignable to type 'string | symbol'.
      Type 'string | number | symbol' is not assignable to type 'string | symbol'.
        Type 'number' is not assignable to type 'string | symbol'.
          Type 'keyof Events' is not assignable to type 'symbol'.
            Type 'K' is not assignable to type 'symbol'.
              Type 'keyof Events' is not assignable to type 'symbol'.
                Type 'string | number | symbol' is not assignable to type 'symbol'.
                  Type 'string' is not assignable to type 'symbol'.ts(2345)
  </code></pre>
  
  <p>According to my quick googling, <code>keyof</code> operator always infer <code>string | symbol | number</code>. So I changed the code to infer string.</p>
  
  <pre class="lang-js prettyprint-override"><code>on&lt;K extends (keyof Events &amp; string)&gt;(
  </code></pre>
  
  <p>This works for the error above. However, I have got another error:</p>
  
  <pre><code>(parameter) listener: (...data: Events[K]) =&gt; void
  Argument of type '(...data: Events[K]) =&gt; void' is not assignable to parameter of type '(...args: any[]) =&gt; void'.
    Types of parameters 'data' and 'args' are incompatible.
      Type 'any[]' is not assignable to type 'Events[K]'.ts(2345)
  </code></pre>
  
  <p>Is there any way to solve this problem without type assertion.</p>
  
  <hr>
  
  <p>(tsconfig.json)</p>
  
  <pre><code>{
    "include": ["./src"],
    "compilerOptions": {
      "target": "es6",
      "module": "commonjs",
      "outDir": "./dist",
      "strict": true,
      "noImplicitAny": true,
      "strictNullChecks": true,
      "strictFunctionTypes": true,
      "strictBindCallApply": true,
      "strictPropertyInitialization": true,
      "noImplicitThis": true,
      "alwaysStrict": true,
      "noUnusedParameters": true,
      "noImplicitReturns": true,
      "noFallthroughCasesInSwitch": true,
      "esModuleInterop": true,
      "forceConsistentCasingInFileNames": true
    }
  }
  </code></pre>
  
  <p>I have uploaded code on <a href="https://codesandbox.io/s/polished-wildflower-00j1v?fontsize=14&amp;hidenavigation=1&amp;module=%2Fsrc%2Findex.ts&amp;theme=dark" rel="nofollow noreferrer">Codesandbox</a></p>