<p dir="auto">The current construct signature for Promise in es6.d.ts is <a href="https://github.com/Microsoft/TypeScript/blob/e39a2e7/src/lib/es6.d.ts#L3612-L3618">this:</a></p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="new &lt;T&gt;(executor: (resolve: (value?: T | PromiseLike&lt;T&gt;) =&gt; void, reject: (reason?: any) =&gt; void) =&gt; void): Promise&lt;T&gt;;"><pre class="notranslate"><span class="pl-k">new</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">executor</span>: <span class="pl-kos">(</span><span class="pl-s1">resolve</span>: <span class="pl-kos">(</span><span class="pl-s1">value</span>?: <span class="pl-smi">T</span> <span class="pl-c1">|</span> <span class="pl-smi">PromiseLike</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-smi"><span class="pl-k">void</span></span><span class="pl-kos">,</span> <span class="pl-s1">reject</span>: <span class="pl-kos">(</span><span class="pl-s1">reason</span>?: <span class="pl-smi">any</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">)</span>: <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi">T</span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">I would like all of these to definitely compile:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="new Promise(resolve =&gt; resolve()); // (1)
new Promise&lt;void&gt;(resolve =&gt; resolve(null)); // (2)
new Promise&lt;number&gt;(resolve =&gt; resolve(5)); // (3)"><pre class="notranslate"><span class="pl-k">new</span> <span class="pl-smi">Promise</span><span class="pl-kos">(</span><span class="pl-s1">resolve</span> <span class="pl-c1">=&gt;</span> <span class="pl-en">resolve</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">// (1)</span>
<span class="pl-k">new</span> <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">resolve</span> <span class="pl-c1">=&gt;</span> <span class="pl-en">resolve</span><span class="pl-kos">(</span><span class="pl-c1">null</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// (2)</span>
<span class="pl-k">new</span> <span class="pl-smi">Promise</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-s1">resolve</span> <span class="pl-c1">=&gt;</span> <span class="pl-en">resolve</span><span class="pl-kos">(</span><span class="pl-c1">5</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// (3)</span></pre></div>
<p dir="auto">... these to maybe compile:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="new Promise&lt;void&gt;(resolve =&gt; resolve()); // (4)
new Promise(resolve =&gt; resolve(5)); // (5), and also inferred as Promise&lt;number&gt;"><pre class="notranslate"><span class="pl-k">new</span> <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-s1">resolve</span> <span class="pl-c1">=&gt;</span> <span class="pl-en">resolve</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">// (4)</span>
<span class="pl-k">new</span> <span class="pl-smi">Promise</span><span class="pl-kos">(</span><span class="pl-s1">resolve</span> <span class="pl-c1">=&gt;</span> <span class="pl-en">resolve</span><span class="pl-kos">(</span><span class="pl-c1">5</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// (5), and also inferred as Promise&lt;number&gt;</span></pre></div>
<p dir="auto">... and this to not compile:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="new Promise&lt;number&gt;(resolve =&gt; resolve()); // (6)"><pre class="notranslate"><span class="pl-k">new</span> <span class="pl-smi">Promise</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-s1">resolve</span> <span class="pl-c1">=&gt;</span> <span class="pl-en">resolve</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">// (6)</span></pre></div>
<p dir="auto">Currently all of these compile, including (6), although (5) is inferred as <code class="notranslate">Promise&lt;{}&gt;</code></p>
<p dir="auto">Changing <code class="notranslate">value</code> to required makes (6) not compile, but also breaks (1) and (4). By then adding one more overload:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="new (executor: (resolve: () =&gt; void, reject: (reason?: any) =&gt; void) =&gt; void): Promise&lt;void&gt;;
new &lt;T&gt;(executor: (resolve: (value: T | PromiseLike&lt;T&gt;) =&gt; void, reject: (reason?: any) =&gt; void) =&gt; void): Promise&lt;T&gt;;"><pre class="notranslate"><span class="pl-k">new</span> <span class="pl-kos">(</span><span class="pl-s1">executor</span>: <span class="pl-kos">(</span><span class="pl-s1">resolve</span>: <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">,</span> <span class="pl-s1">reject</span>: <span class="pl-kos">(</span><span class="pl-s1">reason</span>?: <span class="pl-smi">any</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">)</span>: <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span>
<span class="pl-k">new</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">executor</span>: <span class="pl-kos">(</span><span class="pl-s1">resolve</span>: <span class="pl-kos">(</span><span class="pl-s1">value</span>: <span class="pl-smi">T</span> <span class="pl-c1">|</span> <span class="pl-smi">PromiseLike</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-smi"><span class="pl-k">void</span></span><span class="pl-kos">,</span> <span class="pl-s1">reject</span>: <span class="pl-kos">(</span><span class="pl-s1">reason</span>?: <span class="pl-smi">any</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">)</span>: <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi">T</span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">(1) to (3) will continue compiling, and (4) to (6) will stop compiling. Since (5) was not being inferred correctly anyway, I think it's okay that it won't compile. Losing (4) is perhaps the bigger problem, since it will break existing code that uses it.</p>
<hr>
<p dir="auto">PromiseConstructor.resolve <a href="https://github.com/Microsoft/TypeScript/blob/e39a2e7/src/lib/es6.d.ts#L3650-L3661">already does this</a> - it has one overload with no type parameter and no parameter that returns <code class="notranslate">Promise&lt;void&gt;</code>, and one with a type parameter and a required parameter that returns <code class="notranslate">Promise&lt;T&gt;</code>. I seem to remember Promise's construct signature also did used to do the same at one point (maybe back when it was in lib.d.ts?) which is why I was surprised to discover that it doesn't now.</p>
<p dir="auto">This proposal is based on a working implementation at:<br>
<a href="https://github.com/yortus/TypeScript/tree/preprocessor-directives">https://github.com/yortus/TypeScript/tree/preprocessor-directives</a></p>
<h2 dir="auto">Problem Scenario</h2>
<p dir="auto">Whilst the TypeScript compiler has some options to control what to <em>emit</em> for a particular source file, it currently has limited support for controlling what is <em>scanned</em> into the compiler from a particular source file. A source file is either included in its entirety, or not at all.</p>
<p dir="auto">This makes some scenarios difficult. For instance, there are two default lib files, <code class="notranslate">lib.d.ts</code>, and <code class="notranslate">lib.es6.d.ts</code>. A program may be compiled against one, or the other, or neither. If only <em>some</em> ES6 typings are desired, then either they must <em>all</em> be taken (using <code class="notranslate">lib.es6.d.ts</code>), or a custom set of core typings must be maintained with the project.</p>
<p dir="auto">Even if the core lib files were further subdivided into smaller modular files that could be selectively included in a build, problems would remain. For instance, consider an ES6 built-in, <code class="notranslate">WeakMap</code>, which has members that use the ES6 <code class="notranslate">Symbol</code> spec and the ES6 <code class="notranslate">Iterable</code> spec. How many files must the <code class="notranslate">WeakMap</code> definition be broken into to keep the lib files feature-modular?</p>
<p dir="auto">Related scenarios have been discussed in other issues:</p>
<ul dir="auto">
<li>(<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="105078292" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/4671" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/4671/hovercard" href="https://github.com/microsoft/TypeScript/issues/4671">#4671</a>) Conditional compilation</li>
<li>(<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="91786018" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/3670" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/3670/hovercard" href="https://github.com/microsoft/TypeScript/issues/3670">#3670</a>) Preprocessor directives proposal</li>
<li>(<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="89060191" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/3538" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/3538/hovercard" href="https://github.com/microsoft/TypeScript/issues/3538">#3538</a>) Proposal: Conditional Compilation</li>
<li>(<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="40203848" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/449" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/449/hovercard" href="https://github.com/microsoft/TypeScript/issues/449">#449</a>) Support conditional compilation</li>
</ul>
<p dir="auto">This proposal focuses on the <code class="notranslate">lib.d.ts</code> modularity problem, since that was the core requirement for the related proposal (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="105411159" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/4692" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/4692/hovercard" href="https://github.com/microsoft/TypeScript/issues/4692">#4692</a>) that motivated the <a href="https://github.com/yortus/TypeScript/tree/preprocessor-directives">working implementation</a>.</p>
<h2 dir="auto">Workarounds</h2>
<p dir="auto">With regards to compiling core typings for <em>only some</em> ES6 features, some workarounds are:</p>
<ul dir="auto">
<li>compile with <code class="notranslate">--target ES5</code> and selectively add ES6 typings in separately maintained files (eg from DefinitelyTyped).</li>
<li>compile with <code class="notranslate">--target ES6</code> and be careful to avoid referencing unsupported ES6 features (the compiler won't issue any errors).</li>
<li>compile with <code class="notranslate">--noLib</code> and manually maintain custom core typings in your own project.</li>
</ul>
<p dir="auto">For other scenarios, such as supporting <code class="notranslate">DEBUG</code> builds or <code class="notranslate">IOS</code> builds etc, a common practice is to use a single codebase with conditional execution to differentiate behaviour in different environments. This generally works well, except if conditional <code class="notranslate">require(...)</code>s are needed, as these can be a problem for some module systems that statically analyse module dependencies.</p>
<h2 dir="auto">Proposed Solution</h2>
<p dir="auto">This proposal adds a new kind of syntax for <em>preprocessor directives</em>.</p>
<h3 dir="auto">Preprocessor Directive Syntax</h3>
<p dir="auto">A preprocesor directive:</p>
<ul dir="auto">
<li>begins with a '#' followed by the directive identifier, eg <code class="notranslate">#if</code>, <code class="notranslate">#endif</code></li>
<li><em>must</em> be the first non-whitespace element on its line</li>
<li><em>must not</em> be followed on the same line by anything other than whitespace and/or a single-line comment</li>
</ul>
<p dir="auto"><strong>Valid:</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="    #if DEBUG
#endif // end of debug section
"><pre class="notranslate"><code class="notranslate">    #if DEBUG
#endif // end of debug section

</code></pre></div>
<p dir="auto"><strong>Invalid:</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="}  #if DEBUG
#if X  foo(); #endif 
"><pre class="notranslate"><code class="notranslate">}  #if DEBUG
#if X  foo(); #endif 

</code></pre></div>
<h3 dir="auto">Directives with Arguments</h3>
<p dir="auto">A preprocessor directive may take an argument. If so, the argument appears after the directive identifier on the same line. The directive identifier and its argument must be separated by at least one whitespace character.</p>
<p dir="auto">Under this proposal, only <code class="notranslate">#if</code> takes an argument, which must be an identifier. An extended proposal may expand argument syntax to include preprocessor symbol expressions.</p>
<h3 dir="auto">Contextual Interpretation</h3>
<p dir="auto">If a syntactically valid preprocessor directive appears inside a multiline comment or a multiline string, it is not considered a preprocessor directive. It remains a normal part of the enclosing comment or string.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="/*
   The next line is NOT a preprocessor directive
   #if XYZ
*/"><pre class="notranslate"><code class="notranslate">/*
   The next line is NOT a preprocessor directive
   #if XYZ
*/
</code></pre></div>
<h3 dir="auto">Preprocessor Symbols</h3>
<p dir="auto">A preprocessor symbol is an identifier used with some directives (only <code class="notranslate">#if</code> under this proposal). Preprocessor symbols have no values, they are simply <em>defined</em> or <em>undefined</em>. Under this proposal, the only way to define a preprocessor symbol is using the <code class="notranslate">define</code> compiler option (see below).</p>
<p dir="auto">Preprocessor symbols are in a completely separate space to all other identifiers in source code; they may have the same names as source code identifiers, but they never clash with them.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="#if DEBUG
#if SomeFeature
#if __condition
#if TARGET_HAS_ITERABLES"><pre class="notranslate"><code class="notranslate">#if DEBUG
#if SomeFeature
#if __condition
#if TARGET_HAS_ITERABLES
</code></pre></div>
<h3 dir="auto"><code class="notranslate">#if</code> and <code class="notranslate">#endif</code></h3>
<p dir="auto">The <code class="notranslate">#if</code> and <code class="notranslate">#endif</code> preprocessor directives signal the start and end of a block of conditionally compiled source code. <code class="notranslate">#if</code> must be given a <em>preprocessor symbol</em> as an argument. <code class="notranslate">#endif</code> takes no argument. Each <code class="notranslate">#if</code> in a file must have a matching <code class="notranslate">#endif</code> on a subsequent line in that file.</p>
<p dir="auto">When the TypeScript compiler encounters an <code class="notranslate">#if</code> directive, it evaluates its preprocessor symbol against a list of defined symbols. If the symbol is defined, then the TypeScript scanner continues scanning the source file normally, as if the directive was not present. If the symbol is <em>not</em> defined, then the compiler skips all the source code down to the matching <code class="notranslate">#endif</code> without compiling it.</p>
<p dir="auto"><code class="notranslate">#if...#endif</code> blocks may be nested. Inner blocks will be unconditionally skipped if their outer block is being skipped.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="#if HAS_FOO
foo();
#endif

#if HAS_FOO
foo();
#if HAS_BAR
foo() + bar();
#endif
#endif"><pre class="notranslate"><code class="notranslate">#if HAS_FOO
foo();
#endif

#if HAS_FOO
foo();
#if HAS_BAR
foo() + bar();
#endif
#endif
</code></pre></div>
<h3 dir="auto">The <code class="notranslate">define</code> Compiler Option</h3>
<p dir="auto">Preprocessor symbols may be defined at compile time using the <code class="notranslate">define</code> compiler option, which takes a comma-separated list of identifiers.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="tsc --define FOO,bar

{
    &quot;target&quot;: &quot;es6&quot;,
    &quot;define&quot;: &quot;DEBUG,__foo,ABC&quot;
}"><pre class="notranslate"><code class="notranslate">tsc --define FOO,bar

{
    "target": "es6",
    "define": "DEBUG,__foo,ABC"
}
</code></pre></div>
<h3 dir="auto">Possible Extensions</h3>
<p dir="auto">This proposal is limited to a small set of features that are useful on their own, but may be expanded.</p>
<p dir="auto">In particular, <code class="notranslate">#if</code> and <code class="notranslate">#endif</code> alone are sufficient to address the <code class="notranslate">lib.d.ts</code> problem described above, as evidenced in the <a href="https://github.com/yortus/TypeScript/tree/granular-targeting">working implementation</a> of <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="105411159" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/4692" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/4692/hovercard" href="https://github.com/microsoft/TypeScript/issues/4692">#4692</a>. The ability to nest <code class="notranslate">#if...#endif</code> blocks effectively allows logical ANDing of preprocesor symbols.</p>
<p dir="auto">Possible extensions include:</p>
<ul dir="auto">
<li><code class="notranslate">#define</code> and <code class="notranslate">#undef</code> directives to add/remove preprocessor symbols from within source code. However the question of symbol scope then arises.</li>
<li>Unary and binary expressions involving preprocessor symbols, such as <code class="notranslate">!</code> (logical NOT), <code class="notranslate">&amp;&amp;</code> logical AND, and <code class="notranslate">||</code> logical OR.</li>
</ul>
<h2 dir="auto">Backward Compatibility, Design Impact, Performance, etc</h2>
<ul dir="auto">
<li>There is no impact on existing TypeScript projects. The preprocessor directives only modify the compiler's behaviour if they are explicitly used.</li>
<li>Preprocessor directives introduce new language syntax, but do not affect any existing language features.</li>
<li>There is negligable impact on compiler performance.</li>
<li>The hash symbol is only used in shebang trivia at present. There is no syntax clash. But could it be used for something in a future ES standard?</li>
</ul>
<h2 dir="auto">Remaining Work and Questions</h2>
<p dir="auto">The <a href="https://github.com/yortus/TypeScript/tree/preprocessor-directives">working implementation</a> implements preprocessor directives in the TypeScript scanner, since they are really a filter on incoming tokens. This works fairly well for this limited proposal, but questions arise if extensions were added:</p>
<ul dir="auto">
<li>if <code class="notranslate">#define/#undef</code> were added, how should preprocessor symbols be scoped? Global? Per file? The scanner has very limited control over scoping. Hence currently preprocessor symbols are all globally scoped and provided using a compiler option (or internally generated by the compiler).</li>
<li>Supporting expressions as preprocessor arguments would add complexity to the scanner, as it would need to parse a mini grammar for the expression, which would be atypical at the lexer stage. But certainly doable.</li>
</ul>