<p dir="auto"><em>This proposal serves, at this point, as an trial-and-error experience for myself and to give you guys some ideas to play with.. I understand this may not be 'it' in current form (or not at all) so don't just stamp it with that dreaded "DECLINED" thing. I'll just close it myself after you explain to me how bad it is and why.. and all the edge cases..(e.g. <a href="https://github.com/Microsoft/TypeScript/pull/3516" data-hovercard-type="pull_request" data-hovercard-url="/microsoft/TypeScript/pull/3516/hovercard">expressions in extends clauses</a> etc.) or who knows, I may be even able to fix it later..</em></p>
<p dir="auto">Let <code class="notranslate">A</code> and <code class="notranslate">B</code> be two classes and <code class="notranslate">a: A</code>, <code class="notranslate">b: B</code>.</p>
<p dir="auto">For the structurally valid assignment <code class="notranslate">a = b</code>.</p>
<p dir="auto">Either one of the following conditions should hold to error:</p>
<ol dir="auto">
<li><code class="notranslate">B</code> is a strict nominal ancestor of <code class="notranslate">A</code>.</li>
<li><code class="notranslate">A</code> is a nominal ancestor of <code class="notranslate">B</code> but with an incompatible generic signature.</li>
</ol>
<p dir="auto">Examples:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class Base {
    action = &quot;START WORLD WAR III&quot;;
}

class Derived extends Base {
    action = &quot;SAVE THE WORLD&quot;;
}

function giveMeDerived(shouldBeDerived: Derived) {
    performAction(shouldBeDerived.action);
}

giveMeDerived(new Base()); // Error! The compiler has saved the world!"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-smi">Base</span> <span class="pl-kos">{</span>
    <span class="pl-c1">action</span> <span class="pl-c1">=</span> <span class="pl-s">"START WORLD WAR III"</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">class</span> <span class="pl-smi">Derived</span> <span class="pl-k">extends</span> <span class="pl-smi">Base</span> <span class="pl-kos">{</span>
    <span class="pl-c1">action</span> <span class="pl-c1">=</span> <span class="pl-s">"SAVE THE WORLD"</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-en">giveMeDerived</span><span class="pl-kos">(</span><span class="pl-s1">shouldBeDerived</span>: <span class="pl-smi">Derived</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-en">performAction</span><span class="pl-kos">(</span><span class="pl-s1">shouldBeDerived</span><span class="pl-kos">.</span><span class="pl-c1">action</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-en">giveMeDerived</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-smi">Base</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">// Error! The compiler has saved the world!</span></pre></div>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class Bag&lt;T&gt; {
    items = [];
}

function putStringsInTheBag(bag: Bag&lt;string&gt;) {
    bag.items.push(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;);
}

let bagOfNumbers = new Bag&lt;number&gt;();
putStringsInTheBag(bagOfNumbers); // Error! no strings going in this bag.. "><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-smi">Bag</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-c1">=</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">function</span> <span class="pl-en">putStringsInTheBag</span><span class="pl-kos">(</span><span class="pl-s1">bag</span>: <span class="pl-smi">Bag</span><span class="pl-kos">&lt;</span><span class="pl-smi">string</span><span class="pl-kos">&gt;</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-s1">bag</span><span class="pl-kos">.</span><span class="pl-c1">items</span><span class="pl-kos">.</span><span class="pl-en">push</span><span class="pl-kos">(</span><span class="pl-s">"a"</span><span class="pl-kos">,</span> <span class="pl-s">"b"</span><span class="pl-kos">,</span> <span class="pl-s">"c"</span><span class="pl-kos">,</span> <span class="pl-s">"d"</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">bagOfNumbers</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-smi">Bag</span><span class="pl-kos">&lt;</span><span class="pl-smi">number</span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-en">putStringsInTheBag</span><span class="pl-kos">(</span><span class="pl-s1">bagOfNumbers</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// Error! no strings going in this bag.. </span></pre></div>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class Bag&lt;T&gt; {
    items = [];
}

class BagWithMethod&lt;V&gt; extends Bag&lt;V&gt; {
    putItem(item: V) {
        this.items.push(item);
    }
}

let bagWithMethod = new BagWithMethod&lt;number&gt;();
bagWithMethod.putItem(42);

let evilBag: Bag&lt;string&gt; = bagWithMethod; // Error! evil bag detected!"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-smi">Bag</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-c1">=</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">class</span> <span class="pl-smi">BagWithMethod</span><span class="pl-c1">&lt;</span><span class="pl-smi">V</span><span class="pl-c1">&gt;</span> <span class="pl-k">extends</span> <span class="pl-smi">Bag</span><span class="pl-kos">&lt;</span><span class="pl-smi">V</span><span class="pl-kos">&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-en">putItem</span><span class="pl-kos">(</span><span class="pl-s1">item</span>: <span class="pl-smi">V</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">items</span><span class="pl-kos">.</span><span class="pl-en">push</span><span class="pl-kos">(</span><span class="pl-s1">item</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">let</span> <span class="pl-s1">bagWithMethod</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-smi">BagWithMethod</span><span class="pl-kos">&lt;</span><span class="pl-smi">number</span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-s1">bagWithMethod</span><span class="pl-kos">.</span><span class="pl-en">putItem</span><span class="pl-kos">(</span><span class="pl-c1">42</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

<span class="pl-k">let</span> <span class="pl-s1">evilBag</span>: <span class="pl-smi">Bag</span><span class="pl-kos">&lt;</span><span class="pl-smi">string</span><span class="pl-kos">&gt;</span> <span class="pl-c1">=</span> <span class="pl-s1">bagWithMethod</span><span class="pl-kos">;</span> <span class="pl-c">// Error! evil bag detected!</span></pre></div>
<p dir="auto"><em>Note: this could be opt-in through a compiler flag, e.g. <code class="notranslate">--nominalClassChecks</code>..</em></p>
<p dir="auto">Proposal: support non-structural typing (e.g. new user-defined base-types, or some form of basic nominal typing). This allows programmer to have more refined types supporting frequently used idioms such as:</p>
<ol dir="auto">
<li>
<p dir="auto">Indexes that come from different tables. Because all indexes are strings (or numbers), it's easy to use the an index variable (intended for one table) with another index variable intended for a different table. Because indexes are the same type, no error is given. If we have abstract index classes this would be fixed.</p>
</li>
<li>
<p dir="auto">Certain classes of functions (e.g. callbacks) can be important to be distinguished even though they have the same type. e.g. "() =&gt; void" often captures a side-effect producing function. Sometimes you want to control which ones are put into an event handler. Currently there's no way to type-check them.</p>
</li>
<li>
<p dir="auto">Consider having 2 different interfaces that have different optional parameters but the same required one. In typescript you will not get a compiler error when you provide one but need the other. Sometimes this is ok, but very often this is very not ok and you would love to have a compiler error rather than be confused at run-time.</p>
</li>
</ol>
<p dir="auto">Proposal (with all type-Error-lines removed!):</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="// Define FooTable and FooIndex
nominal FooIndex = string;  // Proposed new kind of nominal declaration.
interface FooTable {
  [i: FooIndex]: { foo: number };
}
let s1: FooIndex;
let t1: FooTable;

// Define BarTable and BarIndex
nominal BarIndex = string; // Proposed new kind of nominal declaration.
interface BarTable {
  [i: BarIndex]: { bar: string };
}
let s2: BarIndex;
let t2: BarTable;

// For assignment from base-types and basic structures: no type-overloading is needed.
s1 = 'foo1';
t1 = {};
t1[s1] = { foo: 1 };

s2 = 'bar1';
t2 = { 'bar1': { bar: 'barbar' }};

console.log(s2 = s1); // Proposed to be type error.
console.log(s2 == s1); // Proposed to be type error.
console.log(s2 === s1); // Proposed to be type error.

t1[s2].foo = 100; // Gives a runtime error. Proposed to be type error.
t1[s1].foo = 100;

function BadFooTest(t: FooTable) {
  if (s2 in t) {  // Proposed to be type error.
    console.log('cool');
    console.log(t[s2].foo); // Proposed to be type error.
  }
}

function GoodBarTest(t: BarTable) {
  if (s2 in t) {
    console.log('cool');
    console.log(t[s2].bar);
  }
}

BadFooTest(t1); // Gives runtime error;
BadFooTest(t2); // No runtime error, Proposed to be type error.
GoodBarTest(t1); // Gives runtime error; Proposed to be type error.
GoodBarTest(t2);"><pre class="notranslate"><code class="notranslate">// Define FooTable and FooIndex
nominal FooIndex = string;  // Proposed new kind of nominal declaration.
interface FooTable {
  [i: FooIndex]: { foo: number };
}
let s1: FooIndex;
let t1: FooTable;

// Define BarTable and BarIndex
nominal BarIndex = string; // Proposed new kind of nominal declaration.
interface BarTable {
  [i: BarIndex]: { bar: string };
}
let s2: BarIndex;
let t2: BarTable;

// For assignment from base-types and basic structures: no type-overloading is needed.
s1 = 'foo1';
t1 = {};
t1[s1] = { foo: 1 };

s2 = 'bar1';
t2 = { 'bar1': { bar: 'barbar' }};

console.log(s2 = s1); // Proposed to be type error.
console.log(s2 == s1); // Proposed to be type error.
console.log(s2 === s1); // Proposed to be type error.

t1[s2].foo = 100; // Gives a runtime error. Proposed to be type error.
t1[s1].foo = 100;

function BadFooTest(t: FooTable) {
  if (s2 in t) {  // Proposed to be type error.
    console.log('cool');
    console.log(t[s2].foo); // Proposed to be type error.
  }
}

function GoodBarTest(t: BarTable) {
  if (s2 in t) {
    console.log('cool');
    console.log(t[s2].bar);
  }
}

BadFooTest(t1); // Gives runtime error;
BadFooTest(t2); // No runtime error, Proposed to be type error.
GoodBarTest(t1); // Gives runtime error; Proposed to be type error.
GoodBarTest(t2);
</code></pre></div>