<p dir="auto">When making higher order functions that return functions, type parameters are not preserved in return type. Notice in the following example how <code class="notranslate">identityM</code> has the generic type parameter <code class="notranslate">A</code> changed into <code class="notranslate">{}</code>, resulting in a loss of type safety in the rest of the code.</p>
<p dir="auto">Ideally <code class="notranslate">identityM</code> would have the type <code class="notranslate">&lt;A&gt;(a: A) =&gt; A</code>. If this isn't possible, issuing a compiler warning (or error) about loss of genericity would be very helpful in tracking down errors like this.</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="function mirror&lt;A, B&gt;(f: (a: A) =&gt; B): (a: A) =&gt; B { return f; }
function identity&lt;A&gt;(a: A): A { return a; }
var identityM = mirror(identity); // type: (a: {}) =&gt; {}

var x = 1;
var y = identity(x); // type: number
var z = identityM(x); // type: {}"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-en">mirror</span><span class="pl-c1">&lt;</span><span class="pl-smi">A</span><span class="pl-kos">,</span> <span class="pl-smi">B</span><span class="pl-c1">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">f</span>: <span class="pl-kos">(</span><span class="pl-s1">a</span>: <span class="pl-smi">A</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">B</span><span class="pl-kos">)</span>: <span class="pl-kos">(</span><span class="pl-s1">a</span>: <span class="pl-smi">A</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">B</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-s1">f</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-k">function</span> <span class="pl-en">identity</span><span class="pl-c1">&lt;</span><span class="pl-smi">A</span><span class="pl-c1">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">a</span>: <span class="pl-smi">A</span><span class="pl-kos">)</span>: <span class="pl-smi">A</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-s1">a</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-k">var</span> <span class="pl-s1">identityM</span> <span class="pl-c1">=</span> <span class="pl-en">mirror</span><span class="pl-kos">(</span><span class="pl-s1">identity</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// type: (a: {}) =&gt; {}</span>

<span class="pl-k">var</span> <span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-c1">1</span><span class="pl-kos">;</span>
<span class="pl-k">var</span> <span class="pl-s1">y</span> <span class="pl-c1">=</span> <span class="pl-en">identity</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// type: number</span>
<span class="pl-k">var</span> <span class="pl-s1">z</span> <span class="pl-c1">=</span> <span class="pl-en">identityM</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// type: {}</span></pre></div>
<p dir="auto">Motivating example (one of many):</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="function compare&lt;T&gt;(lhs: T, rhs: T): boolean {
  return lhs === rhs;
}

if(compare('1', 1)) { // Expected: Error -- I made 'compare' generic for a reason!
  /* ... */
}"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-en">compare</span><span class="pl-c1">&lt;</span><span class="pl-smi">T</span><span class="pl-c1">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">lhs</span>: <span class="pl-smi">T</span><span class="pl-kos">,</span> <span class="pl-s1">rhs</span>: <span class="pl-smi">T</span><span class="pl-kos">)</span>: <span class="pl-smi">boolean</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-s1">lhs</span> <span class="pl-c1">===</span> <span class="pl-s1">rhs</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">if</span><span class="pl-kos">(</span><span class="pl-en">compare</span><span class="pl-kos">(</span><span class="pl-s">'1'</span><span class="pl-kos">,</span> <span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-c">// Expected: Error -- I made 'compare' generic for a reason!</span>
  <span class="pl-c">/* ... */</span>
<span class="pl-kos">}</span></pre></div>
<h3 dir="auto">Proposal</h3>
<p dir="auto">When generic type inference performs its Best Common Type operation (4.12.2), it should be an error if this returns <code class="notranslate">{}</code> when <code class="notranslate">{}</code> was not one of the input types.</p>
<p dir="auto">This is entirely consistent with other behavior already in the compiler:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var foo = bar ? '1' : 1; // Error, no BCT between '1' and 1
function fn() { // Error, no BCT between '1' and 1
  if(foo) {
    return '1';
  } else {
    return 1;
  }
}"><pre class="notranslate"><span class="pl-k">var</span> <span class="pl-s1">foo</span> <span class="pl-c1">=</span> <span class="pl-s1">bar</span> ? <span class="pl-s">'1'</span> : <span class="pl-c1">1</span><span class="pl-kos">;</span> <span class="pl-c">// Error, no BCT between '1' and 1</span>
<span class="pl-k">function</span> <span class="pl-en">fn</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-c">// Error, no BCT between '1' and 1</span>
  <span class="pl-k">if</span><span class="pl-kos">(</span><span class="pl-s1">foo</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-s">'1'</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-c1">1</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<h3 dir="auto">Open Questions</h3>
<p dir="auto">From @KamyarNazeri -- should this apply when there are zero input types? e.g.:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class List&lt;T&gt; {
    items: T[];
}

var x = new List(); // Probably want an error here"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-smi">List</span><span class="pl-c1">&lt;</span><span class="pl-smi">T</span><span class="pl-c1">&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-c1">items</span>: <span class="pl-smi">T</span><span class="pl-kos">[</span><span class="pl-kos">]</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">var</span> <span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-smi">List</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// Probably want an error here</span></pre></div>
<p dir="auto">That seems desirable, but has some collateral damage:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="function foo&lt;T&gt;(x: number, a?: T, b?: T) { }
foo(0); // Error, zero input types provided for inference"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-en">foo</span><span class="pl-c1">&lt;</span><span class="pl-smi">T</span><span class="pl-c1">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">x</span>: <span class="pl-smi">number</span><span class="pl-kos">,</span> <span class="pl-s1">a</span>?: <span class="pl-smi">T</span><span class="pl-kos">,</span> <span class="pl-s1">b</span>?: <span class="pl-smi">T</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-kos">}</span>
<span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// Error, zero input types provided for inference</span></pre></div>
<p dir="auto">This would come up slightly more often than naively expected because many .d.ts authors (mistakenly) create generic types which don't actually consume their type parameters. Perhaps this warning would effectively discourage them from doing so?</p>