<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class C {
   p: number; // Should be an error under --strictNullChecks
   method() {
    this.p; 
  }
}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-smi">C</span> <span class="pl-kos">{</span>
   <span class="pl-c1">p</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span> <span class="pl-c">// Should be an error under --strictNullChecks</span>
   <span class="pl-en">method</span><span class="pl-kos">(</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">p</span><span class="pl-kos">;</span> 
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto"><em>[A search yields this addresses <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="136845222" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/7271" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/7271/hovercard" href="https://github.com/microsoft/TypeScript/issues/7271">#7271</a>. However, I found that out only after it was written, but still decided to post it separately, as I felt it wouldn't gain enough exposure there]</em></p>
<p dir="auto">Since TS classes are purely structural, there is no current way to precisely model the <code class="notranslate">instanceof</code> runtime expression, which is the idiomatic way that instances of classes are tested at runtime:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class A {
    prop: number;
}

class B {
    prop: number;
}

function giveMeA(x: A) {
    if (!(x instanceof A))
        throw new TypeError(&quot;This is not A!&quot;);
}

giveMeA(new A()); // Compiles and works
giveMeA(new B()); // Compiles but errors in runtime: &quot;This is not A!&quot;"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-smi">A</span> <span class="pl-kos">{</span>
    <span class="pl-c1">prop</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">class</span> <span class="pl-smi">B</span> <span class="pl-kos">{</span>
    <span class="pl-c1">prop</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-s1">x</span>: <span class="pl-smi">A</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-c1">!</span><span class="pl-kos">(</span><span class="pl-s1">x</span> <span class="pl-k">instanceof</span> <span class="pl-smi">A</span><span class="pl-kos">)</span><span class="pl-kos">)</span>
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">TypeError</span><span class="pl-kos">(</span><span class="pl-s">"This is not A!"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-smi">A</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">// Compiles and works</span>
<span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-smi">B</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">// Compiles but errors in runtime: "This is not A!"</span></pre></div>
<h3 dir="auto">Proposed solution</h3>
<p dir="auto"><code class="notranslate">instanceof T</code>, where <code class="notranslate">T</code> is a class or possibly a function type, is a strict subtype of <code class="notranslate">T</code> that only includes the nominal reference to <code class="notranslate">T</code>, or any one of its nominal subtypes, and excludes all other structurally compatible types.</p>
<p dir="auto">It is used as follows:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class A {
    prop: number;
}

class B {
    prop: number;
}

function giveMeA(x: instanceof A) {
    if (!(x instanceof A))
        throw new TypeError(&quot;This is not A!&quot;);
}

giveMeA(new A()); // Compiles and works
giveMeA(new B()); // Compilation error: &quot;[instanceof] B is not assignable to type 'instanceof A'&quot;"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-smi">A</span> <span class="pl-kos">{</span>
    <span class="pl-c1">prop</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">class</span> <span class="pl-smi">B</span> <span class="pl-kos">{</span>
    <span class="pl-c1">prop</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-s1">x</span>: <span class="pl-smi">instanceof</span> <span class="pl-smi">A</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-c1">!</span><span class="pl-kos">(</span><span class="pl-s1">x</span> <span class="pl-k">instanceof</span> <span class="pl-smi">A</span><span class="pl-kos">)</span><span class="pl-kos">)</span>
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">TypeError</span><span class="pl-kos">(</span><span class="pl-s">"This is not A!"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-smi">A</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">// Compiles and works</span>
<span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-smi">B</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">// Compilation error: "[instanceof] B is not assignable to type 'instanceof A'"</span></pre></div>
<h3 dir="auto">Implicit narrowing through assignment and flow analysis</h3>
<p dir="auto">In most cases, there wouldn't be a need to explicitly state <code class="notranslate">let x: instanceof A</code> as it would be implicitly inferred through code analysis:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="let x = new A(); // Type of 'x' is 'instanceof A'

x = new B(); // Type of 'x' has now been widened back to 'A' 
             // or alternatively implicitly changed to 'instanceof B'!"><pre class="notranslate"><span class="pl-k">let</span> <span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-smi">A</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// Type of 'x' is 'instanceof A'</span>

<span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-smi">B</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// Type of 'x' has now been widened back to 'A' </span>
             <span class="pl-c">// or alternatively implicitly changed to 'instanceof B'!</span></pre></div>
<p dir="auto">The <code class="notranslate">instanceof</code> runtime operator will now be used as a guard that will also cause the type to be narrowed:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="declare var x: A; // Type of 'x' is 'A' - it cannot be narrowed as not enough info is available
declare var y: B; // Type of 'y' is 'B'

if (x instanceof A) {
    // Type of 'x' is now narrowed to `instanceof A`

    x = y; // Type of 'x' is now widened back to 'A'

}
else if (x instanceof B) {
    // Type is now narrowed to `instanceof B`

    x = y; // Type of 'x' is now widened to 'A' (not 'B')
} "><pre class="notranslate"><span class="pl-k">declare</span> <span class="pl-k">var</span> <span class="pl-s1">x</span>: <span class="pl-smi">A</span><span class="pl-kos">;</span> <span class="pl-c">// Type of 'x' is 'A' - it cannot be narrowed as not enough info is available</span>
<span class="pl-k">declare</span> <span class="pl-k">var</span> <span class="pl-s1">y</span>: <span class="pl-smi">B</span><span class="pl-kos">;</span> <span class="pl-c">// Type of 'y' is 'B'</span>

<span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">x</span> <span class="pl-k">instanceof</span> <span class="pl-smi">A</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-c">// Type of 'x' is now narrowed to `instanceof A`</span>

    <span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-s1">y</span><span class="pl-kos">;</span> <span class="pl-c">// Type of 'x' is now widened back to 'A'</span>

<span class="pl-kos">}</span>
<span class="pl-k">else</span> <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">x</span> <span class="pl-k">instanceof</span> <span class="pl-smi">B</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-c">// Type is now narrowed to `instanceof B`</span>

    <span class="pl-s1">x</span> <span class="pl-c1">=</span> <span class="pl-s1">y</span><span class="pl-kos">;</span> <span class="pl-c">// Type of 'x' is now widened to 'A' (not 'B')</span>
<span class="pl-kos">}</span> </pre></div>
<h3 dir="auto">Application on plain constructor functions</h3>
<p dir="auto">The same behavior may also be given to regular functions that are used as constructors with the <code class="notranslate">new</code> keyword (I'm using some proposed syntax here for illustration):</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="function A(this: { prop: number }) {
    this.prop: = 1;
}

function B(this: { prop: number }) {
    this.prop: = 2;
}

function giveMeA(x: instanceof A) {
    if (!(x instanceof A))
        throw new TypeError(&quot;This is not A!&quot;);
}

giveMeA(new A()); // Compiles and works
giveMeA(new B()); // Compilation error: 'instanceof B' is not assignable to 'instanceof A'"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-smi">A</span><span class="pl-kos">(</span><span class="pl-smi">this</span>: <span class="pl-kos">{</span> <span class="pl-c1">prop</span>: <span class="pl-smi">number</span> <span class="pl-kos">}</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">prop</span>: <span class="pl-c1">=</span> <span class="pl-c1">1</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-smi">B</span><span class="pl-kos">(</span><span class="pl-smi">this</span>: <span class="pl-kos">{</span> <span class="pl-c1">prop</span>: <span class="pl-smi">number</span> <span class="pl-kos">}</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">prop</span>: <span class="pl-c1">=</span> <span class="pl-c1">2</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-s1">x</span>: <span class="pl-smi">instanceof</span> <span class="pl-smi">A</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-c1">!</span><span class="pl-kos">(</span><span class="pl-s1">x</span> <span class="pl-k">instanceof</span> <span class="pl-smi">A</span><span class="pl-kos">)</span><span class="pl-kos">)</span>
        <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">TypeError</span><span class="pl-kos">(</span><span class="pl-s">"This is not A!"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-smi">A</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">// Compiles and works</span>
<span class="pl-en">giveMeA</span><span class="pl-kos">(</span><span class="pl-k">new</span> <span class="pl-smi">B</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">// Compilation error: 'instanceof B' is not assignable to 'instanceof A'</span></pre></div>
<h3 dir="auto">Remarks</h3>
<p dir="auto">The more conceptual way to look at this is that it takes nominal subtyping and simply views it as a specialized subdivision of structural subtyping (similarly to the way, say, string literal types are a specialization of string types), and allows both to live together in a backwards compatible way, without requiring explicitly stating a class as particularly being 'nominal' or 'structural'. It doesn't require any compiler switches and most of the time would be effective implicitly through assignment and flow analysis.</p>
<p dir="auto">I have no idea of the difficulty of implementing this, especially the analysis part, but I believe it might be possible, considering flow analysis has already been proven viable and integrated to the next release. I'm aware this may not turn out to be the 'ultimate' solution that would eventually chosen, but I thought this was interesting enough to share.</p>