<p dir="auto">Now that TypeScript does <a href="https://github.com/Microsoft/TypeScript/pull/8010" data-hovercard-type="pull_request" data-hovercard-url="/microsoft/TypeScript/pull/8010/hovercard">control flow based type analysis</a>, and there is a <a href="https://github.com/Microsoft/TypeScript/pull/8652" data-hovercard-type="pull_request" data-hovercard-url="/microsoft/TypeScript/pull/8652/hovercard"><code class="notranslate">never</code> type</a> in the works, is it possible to consider providing better type checking around <code class="notranslate">assert(...)</code> function calls that assert that a variable has a certain type at runtime?</p>
<p dir="auto"><strong>TL;DR: some <code class="notranslate">assert</code> functions are really just type guards that signal via <code class="notranslate">return</code>/<code class="notranslate">throw</code> rather than <code class="notranslate">true</code>/<code class="notranslate">false</code>.</strong> Example:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="assert(typeof s === &quot;string&quot;); // throws if s is not a string
s.length; // s is narrowed to string here..."><pre class="notranslate"><span class="pl-en">assert</span><span class="pl-kos">(</span><span class="pl-k">typeof</span> <span class="pl-s1">s</span> <span class="pl-c1">===</span> <span class="pl-s">"string"</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// throws if s is not a string</span>
<span class="pl-s1">s</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">;</span> <span class="pl-c">// s is narrowed to string here...</span></pre></div>
<h3 dir="auto">Problem</h3>
<p dir="auto">Asserts are common in contract-based programming, but I've also been coming across this scenario regularly whilst traversing JavaScript ASTs based on the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API" rel="nofollow">Parser API</a> (I'm using babel to produce/traverse ASTs).</p>
<p dir="auto">For example, consider the <code class="notranslate">MemberExpression</code>:</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://cloud.githubusercontent.com/assets/1252361/15344361/db71cf24-1cd8-11e6-9ffc-b3e95a0ace35.png"><img src="https://cloud.githubusercontent.com/assets/1252361/15344361/db71cf24-1cd8-11e6-9ffc-b3e95a0ace35.png" alt="memberexpression" style="max-width: 100%;"></a></p>
<p dir="auto">Note we can assume <code class="notranslate">property</code> is an <code class="notranslate">Identifier</code> if <code class="notranslate">computed===false</code>. This is what I'd like to write:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="function foo(expr: MemberExprssion) {
    if (expr.computed) {
        // handle computed case
    }
    else {
        // since computed===false, we know property must be an Identifier
        assert(isIdentifier(expr.property));

        let name = expr.property.name;  // ERROR: name doesn't exist on Identifier|Expression
    }
}"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-s1">expr</span>: <span class="pl-smi">MemberExprssion</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-s1">expr</span><span class="pl-kos">.</span><span class="pl-c1">computed</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-c">// handle computed case</span>
    <span class="pl-kos">}</span>
    <span class="pl-k">else</span> <span class="pl-kos">{</span>
        <span class="pl-c">// since computed===false, we know property must be an Identifier</span>
        <span class="pl-en">assert</span><span class="pl-kos">(</span><span class="pl-en">isIdentifier</span><span class="pl-kos">(</span><span class="pl-s1">expr</span><span class="pl-kos">.</span><span class="pl-c1">property</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">name</span> <span class="pl-c1">=</span> <span class="pl-s1">expr</span><span class="pl-kos">.</span><span class="pl-c1">property</span><span class="pl-kos">.</span><span class="pl-c1">name</span><span class="pl-kos">;</span>  <span class="pl-c">// ERROR: name doesn't exist on Identifier|Expression</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">Unfortunately that doesn't compile, because <code class="notranslate">expr.property</code> does not get narrowed after the <code class="notranslate">assert(...)</code> call.</p>
<p dir="auto">To get the full benefit of control flow analysis currently, you have to expand the assert call inline:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="...
else {
    if (!isIdentifier(expr.property)) {
        throw new AssertionError(`Expected property to be an Identifier`);
    }

    let name = expr.property.name;  // OK
}
..."><pre class="notranslate">...
<span class="pl-k">else</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-en">isIdentifier</span><span class="pl-kos">(</span><span class="pl-s1">expr</span><span class="pl-kos">.</span><span class="pl-c1">property</span><span class="pl-kos">)</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">AssertionError</span><span class="pl-kos">(</span><span class="pl-s">`Expected property to be an Identifier`</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">name</span> <span class="pl-c1">=</span> <span class="pl-s1">expr</span><span class="pl-kos">.</span><span class="pl-c1">property</span><span class="pl-kos">.</span><span class="pl-c1">name</span><span class="pl-kos">;</span>  <span class="pl-c">// OK</span>
<span class="pl-kos">}</span>
...</pre></div>
<p dir="auto">While preparing the typings for <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/babel-core/babel-core.d.ts"><code class="notranslate">babel-core</code></a>, <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/babel-types/babel-types.d.ts"><code class="notranslate">babel-types</code></a> and friends, I noticed that using asserts this way is the norm. <code class="notranslate">babel-types</code> actually provides an <code class="notranslate">assertXXX</code> method for every <code class="notranslate">isXXX</code> method. These <code class="notranslate">assertXXX</code> functions are really just type guards that signal via <code class="notranslate">return</code>/<code class="notranslate">throw</code> rather than <code class="notranslate">true</code>/<code class="notranslate">false</code>.</p>
<h3 dir="auto">Possible Solutions?</h3>
<p dir="auto">Not sure if it's feasible at all! But the new work on <code class="notranslate">never</code> in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="155357716" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/8652" data-hovercard-type="pull_request" data-hovercard-url="/microsoft/TypeScript/pull/8652/hovercard" href="https://github.com/microsoft/TypeScript/pull/8652">#8652</a> suggests a few possibilities.</p>
<h4 dir="auto">Specific assertions: assertIsT(...)</h4>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// normal type guard
function isIdentifier(n: Node): n is Identifier {
    return n.type === 'Identifier';
}

// PROPOSED SYNTAX: assert type guard
function assertIdentifier(n: Node): n is Identifier | never {
    if (n.type !== 'Identifier') {
        throw new AssertionError(`Expected an Identifier`);
    }
}
"><pre class="notranslate"><span class="pl-c">// normal type guard</span>
<span class="pl-k">function</span> <span class="pl-en">isIdentifier</span><span class="pl-kos">(</span><span class="pl-s1">n</span>: <span class="pl-smi">Node</span><span class="pl-kos">)</span>: <span class="pl-s1">n</span> is <span class="pl-smi">Identifier</span> <span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-s1">n</span><span class="pl-kos">.</span><span class="pl-c1">type</span> <span class="pl-c1">===</span> <span class="pl-s">'Identifier'</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-c">// PROPOSED SYNTAX: assert type guard</span>
<span class="pl-k">function</span> <span class="pl-en">assertIdentifier</span><span class="pl-kos">(</span><span class="pl-s1">n</span>: <span class="pl-smi">Node</span><span class="pl-kos">)</span>: <span class="pl-s1">n</span> is <span class="pl-smi">Identifier</span> <span class="pl-c1">|</span> <span class="pl-smi">never</span> <span class="pl-kos">{</span>
    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">n</span><span class="pl-kos">.</span><span class="pl-c1">type</span> <span class="pl-c1">!==</span> <span class="pl-s">'Identifier'</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">AssertionError</span><span class="pl-kos">(</span><span class="pl-s">`Expected an Identifier`</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">The compiler would reason that if this assert call returns at all, then it can safely narrow the variable type in following code.</p>
<h4 dir="auto">General assertions used with type guards: assert(isT(...))</h4>
<p dir="auto">The more general <code class="notranslate">assert(cond: boolean)</code> function would need a different approach and might not be feasible, but here's an idea:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="
    // General case
    declare function assert(cond: boolean): void;

    // PROPOSED SYNTAX: Special overload for expressions of the form assert(isT(x))
    declare function assert&lt;T&gt;(guard: guard is T): void | never;
"><pre class="notranslate">    <span class="pl-c">// General case</span>
    <span class="pl-k">declare</span> <span class="pl-k">function</span> <span class="pl-s1">assert</span><span class="pl-kos">(</span><span class="pl-s1">cond</span>: <span class="pl-smi">boolean</span><span class="pl-kos">)</span>: <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">;</span>

    <span class="pl-c">// PROPOSED SYNTAX: Special overload for expressions of the form assert(isT(x))</span>
    <span class="pl-k">declare</span> <span class="pl-k">function</span> <span class="pl-s1">assert</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">guard</span>: <span class="pl-smi">guard</span> <span class="pl-s1">is</span> <span class="pl-smi">T</span><span class="pl-kos">)</span>: <span class="pl-smi"><span class="pl-k">void</span></span> <span class="pl-c1">|</span> <span class="pl-smi">never</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">For that second <code class="notranslate">assert</code> overload to work, the compiler on seeing <code class="notranslate">assert(isT(x))</code> would have to somehow forward the <code class="notranslate">x is T</code> narrowing from the <code class="notranslate">isT(x)</code> expression to the <code class="notranslate">assert(...)</code> expression at compile-time.</p>
<p dir="auto">Would be great if it also detected/handled things like <code class="notranslate">assert(typeof x == 'string')</code>.</p>
<p dir="auto">Not sure if any of this would meet the cost/benefit bar, but it's just an idea.</p>
<p dir="auto">When writing a node software that should create a batch or symbolic link, we need to put the folowing line at the begining of the .js file:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="#!/bin/node"><pre class="notranslate">#!/bin/node</pre></div>
<p dir="auto">So npm knows it is a node executable script and creates a batch file with all the necessary stuff in it.<br>
If we put it at a typescript file to be transpiled to javascript, it works, but gives annoying compilation error.<br>
I purpose that lines started with <code class="notranslate">#!</code> shoud be treated as a special case of commentary line that should always go to the transpiled file.</p>