title: TypeScript new Array.prototype Generic Function with 'this' parameter
body: <p>I have an Angular 8/TS app, and I'd like to add a <code>.groupBy()</code> function to Array's prototype. I have read up on the TypeScript <a href="https://www.typescriptlang.org/docs/handbook/functions.html" rel="nofollow noreferrer">this parameter</a>, which states that you just add <code>this: &lt;Type&gt;</code> as a fake first parameter to your argument.</p>
  
  <p>This works splendidly <em>inside</em> the method as I'm defining it for the prototype, but anywhere I <em>call</em> the method, my array reverts to type <code>any</code>.</p>
  
  <p>So my problem is, I think, that I'm using an Array (which is generic), and my function takes generic parameters, like so:</p>
  
  <pre><code>if(!Array.prototype.groupBy) {
      Array.prototype.groupBy = function&lt;T, TKey, TResult&gt;(
          this: T[], // here's the magical "this" parameter
          keySelector: (item: T) =&gt; TKey,
          itemSelector: (item: T) =&gt; TResult
      ): Group&lt;TResult, TKey&gt;[] {
  </code></pre>
  
  <p>I have a test method in the same file that calls my new method:</p>
  
  <pre><code>function test(): void {
      let arr: number[] = [ 11, 29, 32, 33, 10, 55 ];
      let groupByTens = arr.groupBy(
          i =&gt; i / 10, // here, "i" is "unknown" which generates a compile error
          i =&gt; i.toString());
  }
  </code></pre>
  
  <p>If I am explicit when I call <code>arr.groupBy&lt;number, number, string&gt;(..)</code>, then it works find, but I don't want to have to explicitly state the generic types. <strong>How can I write my "extension method" in such a way that I won't have to explicitly state the generic types?</strong></p>
  
  <p>Thanks!</p>
  
  <p>And for completeness's sake, here's my full groupBy function:</p>
  
  <pre><code>if(!Array.prototype.groupBy) {
      Array.prototype.groupBy = function&lt;T, TKey, TResult&gt;(
          this: T[],
          keySelector: (item: T) =&gt; TKey,
          itemSelector: (item: T) =&gt; TResult
      ): Group&lt;TResult, TKey&gt;[] {
          let groupDict: any = {};
  
          // HERE, if I provide the "this parameter", then "this" is an Array&lt;T&gt;. If I don't,
          // then "this" is an "any". So the "this parameter" is working just fine inside the
          // function declaration.
          this.forEach((item: T) =&gt; {
              let groupKey = keySelector(item);
  
              let group = groupDict[groupKey];
              if (!group) {
                  group = new Group&lt;TResult, TKey&gt;(groupKey);
                  groupDict[groupKey] = group;
              }
  
              group.members.push(itemSelector(item))
          });
  
          return Object.keys(groupDict)
              .map(key =&gt; &lt;Group&lt;TResult, TKey&gt;&gt;groupDict[key]);
      }
  }
  </code></pre>