<p dir="auto"><em>From <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/alexandrudima/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/alexandrudima">@alexandrudima</a> on January 27, 2016 13:53</em></p>
<p dir="auto">Testing <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="60046420" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/2218" data-hovercard-type="pull_request" data-hovercard-url="/microsoft/TypeScript/pull/2218/hovercard" href="https://github.com/microsoft/TypeScript/pull/2218">#2218</a></p>
<p dir="auto">The duplicate here is not a problem because the code is not in strict mode (also tried running it and node runs it just fine):</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// &quot;use strict&quot;;
var o = { p: 1, p: 2};
console.log(o);"><pre class="notranslate"><span class="pl-c">// "use strict";</span>
<span class="pl-k">var</span> <span class="pl-s1">o</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-c1">p</span>: <span class="pl-c1">1</span><span class="pl-kos">,</span> <span class="pl-c1">p</span>: <span class="pl-c1">2</span><span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s1">o</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://cloud.githubusercontent.com/assets/5047891/12615335/6e88aee4-c505-11e5-8a8e-81ae41632fff.png"><img src="https://cloud.githubusercontent.com/assets/5047891/12615335/6e88aee4-c505-11e5-8a8e-81ae41632fff.png" alt="image" style="max-width: 100%;"></a></p>
<h2 dir="auto">But in ES6 this is not an error anymore?</h2>
<p dir="auto">I have also found the following interesting note at <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode" rel="nofollow">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode</a>:<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://cloud.githubusercontent.com/assets/5047891/12615377/99c5e77a-c505-11e5-90a7-1660fbf5216d.png"><img src="https://cloud.githubusercontent.com/assets/5047891/12615377/99c5e77a-c505-11e5-90a7-1660fbf5216d.png" alt="image" style="max-width: 100%;"></a></p>
<p dir="auto"><em>Copied from original issue: <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="129150001" data-permission-text="Title is private" data-url="https://github.com/microsoft/vscode/issues/2452" data-hovercard-type="issue" data-hovercard-url="/microsoft/vscode/issues/2452/hovercard" href="https://github.com/microsoft/vscode/issues/2452">microsoft/vscode#2452</a></em></p>
<p dir="auto">Apologies in advance, as this might be more of a question, but I'm asking here rather than StackOverflow because there might be some changes to the type checking that could allow this pattern to be easier to express, and I might be encountering some bugs.</p>
<p dir="auto">I'm trying to convert the subclass factory mixin pattern to TS, with enough type info to be able to determine the complete object shape of a class that uses these mixins. With intersection types and f-bounded quantification, it seems like we should be in really good shape, but I ran into several difficulties and maybe some bugs.</p>
<p dir="auto"><strong>TypeScript Version:</strong></p>
<p dir="auto">1.8.0</p>
<p dir="auto"><strong>Code</strong></p>
<p dir="auto">Here's the untyped JS:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="let M1 = (superclass) =&gt; class extends superclass {
  foo() { return 'a string'; }
}

let M2 = (superclass) =&gt; class extends superclass {
  bar() { return 42; }
}

class C = M2(M1(Object)) {
  baz() { return true; }
}

let c = new C();
console.log(c.foo(), c.bar(), c.baz());"><pre class="notranslate"><span class="pl-k">let</span> <span class="pl-c1">M1</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-s1">superclass</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-s">'a string'</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-k">let</span> <span class="pl-c1">M2</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-s1">superclass</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-en">bar</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-c1">42</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-k">class</span> <span class="pl-v">C</span> <span class="pl-c1">=</span> <span class="pl-c1">M2</span><span class="pl-kos">(</span><span class="pl-c1">M1</span><span class="pl-kos">(</span><span class="pl-v">Object</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-en">baz</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-c1">true</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-k">let</span> <span class="pl-s1">c</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">C</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s1">c</span><span class="pl-kos">.</span><span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-s1">c</span><span class="pl-kos">.</span><span class="pl-en">bar</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-s1">c</span><span class="pl-kos">.</span><span class="pl-en">baz</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">When converting to TypeScript, the first issue is that <code class="notranslate">class extends superclass</code> complains that <code class="notranslate">superclass</code> is not a constructor function type, so I introduce an interface for that:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface Constructable {
  new (): Object;
}

let M1 = (superclass: Constructable) =&gt; class extends superclass {
  foo() { return 'a string'; }
}

let M2 = (superclass: Constructable) =&gt; class extends superclass {
  bar() { return 42; }
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-smi">Constructable</span> <span class="pl-kos">{</span>
  <span class="pl-k">new</span> <span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">Object</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">let</span> <span class="pl-smi">M1</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-s1">superclass</span>: <span class="pl-smi">Constructable</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-s">'a string'</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-k">let</span> <span class="pl-smi">M2</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-s1">superclass</span>: <span class="pl-smi">Constructable</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-en">bar</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-c1">42</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">This causes the mixin declarations to pass type checking, but the usage loses type information for nested mixins:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="console.log(c.foo(), c.bar(), c.baz()); // Property 'foo' does not exist on type 'C1'."><pre class="notranslate"><span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s1">c</span><span class="pl-kos">.</span><span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-s1">c</span><span class="pl-kos">.</span><span class="pl-en">bar</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-s1">c</span><span class="pl-kos">.</span><span class="pl-en">baz</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// Property 'foo' does not exist on type 'C1'.</span></pre></div>
<p dir="auto">With intersection types, I hope to be able to type the subclass factories such that they include both the superclass and class they declare. Something like:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="let M1 = &lt;T&gt;(superclass: Constructable&lt;T&gt;): T &amp; M1 =&gt; class extends superclass {
  foo() { return 'a string'; }
}"><pre class="notranslate"><span class="pl-k">let</span> <span class="pl-smi">M1</span> <span class="pl-c1">=</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">superclass</span>: <span class="pl-smi">Constructable</span><span class="pl-kos">&lt;</span><span class="pl-smi">T</span><span class="pl-kos">&gt;</span><span class="pl-kos">)</span>: <span class="pl-smi">T</span> <span class="pl-c1">&amp;</span> <span class="pl-smi">M1</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-s">'a string'</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">Obviously I can't reference <code class="notranslate">M1</code> like this because it refers to the class factory, not the type that the factory returns. I can remove it though:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="let M1 = &lt;T&gt;(superclass: Constructable&lt;T&gt;): T =&gt; class extends superclass {
  foo: string;
}"><pre class="notranslate"><span class="pl-k">let</span> <span class="pl-smi">M1</span> <span class="pl-c1">=</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">superclass</span>: <span class="pl-smi">Constructable</span><span class="pl-kos">&lt;</span><span class="pl-smi">T</span><span class="pl-kos">&gt;</span><span class="pl-kos">)</span>: <span class="pl-smi">T</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-c1">foo</span>: <span class="pl-smi">string</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">and now I get the error: <em>TS2322: Type 'typeof (Anonymous class)' is not assignable to type 'T'.</em></p>
<p dir="auto">Which makes sense, because superclass is a <code class="notranslate">Constructable&lt;T&gt;</code>, not a <code class="notranslate">T</code>, which should be solvable by the new support for f-bounded quantification:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface Constructable&lt;T extends Constructable&lt;T&gt;&gt; {
  new (): T;
}

let M1 = &lt;T extends Constructable&lt;T&gt;&gt;(superclass: T): T =&gt; class extends superclass {
  foo: string;
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-smi">Constructable</span><span class="pl-c1">&lt;</span><span class="pl-smi">T</span> <span class="pl-k">extends</span> <span class="pl-smi">Constructable</span><span class="pl-kos">&lt;</span><span class="pl-smi">T</span><span class="pl-kos">&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-k">new</span> <span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">T</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">let</span> <span class="pl-smi">M1</span> <span class="pl-c1">=</span> <span class="pl-c1">&lt;</span><span class="pl-smi">T</span> <span class="pl-k">extends</span> <span class="pl-smi">Constructable</span><span class="pl-kos">&lt;</span><span class="pl-smi">T</span><span class="pl-kos">&gt;</span><span class="pl-c1">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">superclass</span>: <span class="pl-smi">T</span><span class="pl-kos">)</span>: <span class="pl-smi">T</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-c1">foo</span>: <span class="pl-smi">string</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">But now I get another error on <code class="notranslate">extends superclass</code>: <em>TS2507: Type 'T' is not a constructor function type.</em> even though <code class="notranslate">T</code> should implement <code class="notranslate">Constructable&lt;T&gt;</code> which is a constructor function type. Is this a bug?</p>
<p dir="auto">Another possible bug I ran into is with my attempt at the <code class="notranslate">Constructable</code> interface. As I mentioned, I can get the declaration to (questionably) pass type checking, before using recursive constraints:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface Constructable&lt;T&gt; {
  new (): Object;
}

let M1 = &lt;T&gt;(superclass: Constructable&lt;T&gt;) =&gt; class extends superclass {
  foo() { return 'a string'; }
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-smi">Constructable</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-k">new</span> <span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">Object</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">let</span> <span class="pl-smi">M1</span> <span class="pl-c1">=</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">superclass</span>: <span class="pl-smi">Constructable</span><span class="pl-kos">&lt;</span><span class="pl-smi">T</span><span class="pl-kos">&gt;</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">class</span> <span class="pl-k">extends</span> <span class="pl-s1">superclass</span> <span class="pl-kos">{</span>
  <span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-s">'a string'</span><span class="pl-kos">;</span> <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">but the declaration for <code class="notranslate">new</code> is off, it should be:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface Constructable&lt;T&gt; {
  new (): T;
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-smi">Constructable</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-k">new</span> <span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">T</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">But this triggers the error: <em>TS2509: Base constructor return type 'T' is not a class or interface type.</em></p>
<p dir="auto">neither of these variants fix it:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface Constructable&lt;T extends Object&gt; {
  new (): T;
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-smi">Constructable</span><span class="pl-c1">&lt;</span><span class="pl-smi">T</span> <span class="pl-k">extends</span> <span class="pl-smi">Object</span><span class="pl-c1">&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-k">new</span> <span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">T</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">or:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface Constructable&lt;T&gt; {
  new (): T &amp; Object;
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-smi">Constructable</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-k">new</span> <span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">T</span> <span class="pl-c1">&amp;</span> <span class="pl-smi">Object</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">Even once these issues (which might be my fault, I hope!) are over come, there's another problem of being able to refer to the type returned by a subclass factory. It seems like I would have to define an interface as well as the class expression, which is enough duplicate work to make this pattern very cumbersome to use in TypeScript.</p>
<p dir="auto">Assuming tsc can eventually correctly infer the type returned by a subclass factory <code class="notranslate">M1</code>, it would be great to be able to refer to that type for use in <code class="notranslate">implements</code>, etc.</p>