<p dir="auto">Good progress is made on supporting the case of packages that carry their own typings in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="60991541" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/2338" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/2338/hovercard" href="https://github.com/microsoft/TypeScript/issues/2338">#2338</a>.</p>
<p dir="auto">That proposal doesn't currently deal with modules that do not carry their own typings, a.k.a. all modules that need typings from DefinitelyTyped.</p>
<p dir="auto">This issue is to investigate possibilities of, and propose a (simple) solution for preventing problems when using (multiple versions of) external modules together.</p>
<p dir="auto"><strong>TL;DR</strong></p>
<ol dir="auto">
<li>No longer use <code class="notranslate">declare module "..." { }</code> in typings for external modules</li>
<li>Add an extra step to the resolution logic of <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="60991541" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/2338" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/2338/hovercard" href="https://github.com/microsoft/TypeScript/issues/2338">#2338</a> to look up this type of external typings</li>
</ol>
<p dir="auto"><strong>Example scenario</strong></p>
<ul dir="auto">
<li>myprogram
<ul dir="auto">
<li>mylib
<ul dir="auto">
<li>myutils@1.0</li>
</ul>
</li>
<li>myotherlib
<ul dir="auto">
<li>myutils@2.0</li>
</ul>
</li>
</ul>
</li>
</ul>
<p dir="auto">Suppose that both <code class="notranslate">mylib</code> and <code class="notranslate">myotherlib</code> export a function, that returns a value of a type defined in <code class="notranslate">myutils</code>.</p>
<p dir="auto"><strong>Existing scenario</strong></p>
<p dir="auto">Likely, both <code class="notranslate">mylib</code> and <code class="notranslate">myotherlib</code> will each have a <code class="notranslate">/// &lt;reference path="./typings/tsd.d.ts" /&gt;</code> line, which again has a <code class="notranslate">/// &lt;reference path="./myutils/myutils.d.ts" /&gt;</code> line.</p>
<p dir="auto"><strong>Problem</strong></p>
<p dir="auto">Both libs carry their own version of <code class="notranslate">myutils.d.ts</code>, which works fine as long as they are not used together.</p>
<p dir="auto">However, when they <em>are</em> used together (as in this example), all the <code class="notranslate">/// &lt;reference ...&gt;</code> lines will (need to) be 'merged', which essentially means the <code class="notranslate">declare module "myutils" { ... }</code> from both <code class="notranslate">mylib</code> and <code class="notranslate">myotherlib</code> will end up in the same 'global module name namespace'.</p>
<p dir="auto">This gives a compiler error, and even if the compiler would somehow allow it, would make it impossible to refer to either version of <code class="notranslate">myutils</code>.</p>
<p dir="auto">Additionally, tools like <a href="https://github.com/SitePen/dts-generator"><code class="notranslate">dts-generator</code></a> e.g. include the <code class="notranslate">/// &lt;reference&gt;</code> line inside of the <code class="notranslate">declare</code> blocks, which get ignored by the compiler. This leads to unknown-references when trying to use such a typing, and will typically be solved by adding e.g. a <code class="notranslate">myutils/myutils.d.ts</code> to <code class="notranslate">myprogram/typings/tsd.d.ts</code>, which will be wrong for one of the libs.</p>
<p dir="auto"><strong>Solution</strong></p>
<p dir="auto">External modules have the nice property that there is no 'global namespace' (except the filesystem, maybe), and basically every reference is 'local'.</p>
<p dir="auto">It makes sense to use the same referencing scheme that's used for resolving 'local' external modules (within a package) for 'normal' external modules (different npm packages).</p>
<p dir="auto">Basically, the idea consists of:</p>
<ul dir="auto">
<li>No longer using <code class="notranslate">declare module "..." { }</code> in typings for external modules (to prevent the global namespace clash)</li>
<li>Adding an extra step to the resolution logic of <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="60991541" data-permission-text="Title is private" data-url="https://github.com/microsoft/TypeScript/issues/2338" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/2338/hovercard" href="https://github.com/microsoft/TypeScript/issues/2338">#2338</a> to look up this type of external typings (e.g. in <code class="notranslate">typings/</code> folder)</li>
</ul>
<p dir="auto"><strong>Advantages</strong></p>
<ul dir="auto">
<li>Multiple versions of a package can co-exist in the same program/library</li>
<li><code class="notranslate">/// &lt;reference&gt;</code> lines are no longer needed in most cases (except probably for e.g. importing Mocha's <code class="notranslate">it()</code> and other 'true' globals)</li>
<li>One format to rule them all: external module typings are already generated without <code class="notranslate">declare module "..." { }</code> by <code class="notranslate">tsc</code>, so all 'sorts' of external module typings will look the same</li>
<li>Gradual upgrade path: the <code class="notranslate">tsd.d.ts</code>-way will still work for packages that do still use <code class="notranslate">declare module "..." { }</code> (although it will still have the name-clash problem etc.)</li>
</ul>
<p dir="auto"><strong>Disadvantages</strong></p>
<ul dir="auto">
<li>Typings on e.g. DefinitelyTyped will (gradually) need to be replaced with their non-wrapped version</li>
<li>Extra lookup step in the compiler</li>
<li>Location of these external typings (a.k.a. include path) needs to be configured somewhere and/or sensible default value chosen</li>
</ul>
<p dir="auto"><strong>Example 'new-style' typings</strong></p>
<p dir="auto">Most hand-crafted typings will typically look like:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="/// myutils.d.ts
export interface BarType {
    bar: number;
}
export declare function something(): BarType;"><pre class="notranslate"><span class="pl-c">/// myutils.d.ts</span>
<span class="pl-k">export</span> <span class="pl-k">interface</span> <span class="pl-smi">BarType</span> <span class="pl-kos">{</span>
    <span class="pl-c1">bar</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>
<span class="pl-k">export</span> <span class="pl-k">declare</span> <span class="pl-k">function</span> <span class="pl-s1">something</span><span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">BarType</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">But just to make things more interesting, suppose the typings of <code class="notranslate">myutils</code> look like:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="/// dist/bar.d.ts
export interface BarType {
    bar: number;
}

/// dist/foo.d.ts
import bar = require(&quot;./bar&quot;);
export declare function something(): bar.BarType;"><pre class="notranslate"><span class="pl-c">/// dist/bar.d.ts</span>
<span class="pl-k">export</span> <span class="pl-k">interface</span> <span class="pl-smi">BarType</span> <span class="pl-kos">{</span>
    <span class="pl-c1">bar</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-c">/// dist/foo.d.ts</span>
<span class="pl-k">import</span> <span class="pl-s1">bar</span> <span class="pl-c1">=</span> require<span class="pl-kos">(</span><span class="pl-s">"./bar"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-k">export</span> <span class="pl-k">declare</span> <span class="pl-k">function</span> <span class="pl-s1">something</span><span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-s1">bar</span><span class="pl-kos">.</span><span class="pl-smi">BarType</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Nothing special here, this is what <code class="notranslate">tsc</code> generates today.<br>
(Note: dist/ may be because someone used CoffeeScript, not TypeScript, right? :))</p>
<p dir="auto">Now, the typings dir on e.g. DefinitelyTyped could look like:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="DefinitelyTyped/myutils/latest/dist/bar.d.ts
DefinitelyTyped/myutils/latest/dist/foo.d.ts
DefinitelyTyped/myutils/1.0/dist/bar.d.ts
DefinitelyTyped/myutils/1.0/dist/foo.d.ts"><pre class="notranslate"><code class="notranslate">DefinitelyTyped/myutils/latest/dist/bar.d.ts
DefinitelyTyped/myutils/latest/dist/foo.d.ts
DefinitelyTyped/myutils/1.0/dist/bar.d.ts
DefinitelyTyped/myutils/1.0/dist/foo.d.ts
</code></pre></div>
<p dir="auto">(Or maybe <code class="notranslate">2.0</code> instead of <code class="notranslate">latest</code>, ideas welcome.)</p>
<p dir="auto">And both <code class="notranslate">mylib</code> and <code class="notranslate">myotherlib</code> would have:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="typings/myutils/dist/bar.d.ts
typings/myutils/dist/foo.d.ts"><pre class="notranslate"><code class="notranslate">typings/myutils/dist/bar.d.ts
typings/myutils/dist/foo.d.ts
</code></pre></div>
<p dir="auto"><strong>Looking up .d.ts</strong></p>
<p dir="auto">Given e.g. <code class="notranslate">import { something } from "myutils"</code> the compiler could use <a href="https://github.com/Microsoft/TypeScript/issues/2338#issuecomment-94555288" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/2338/hovercard"><code class="notranslate">myutils/package.json</code>'s <code class="notranslate">main</code> property</a> to get to <code class="notranslate">./dist/foo.js</code>, which in turn would resolve to <code class="notranslate">dist/foo.d.ts</code>.</p>
<p dir="auto">Nice and simple: no need to have support from the authors of <code class="notranslate">myutils</code>, no <code class="notranslate">/// &lt;reference&gt;</code> lines.<br>
And it even supports the <code class="notranslate">import bar  = require("myutils/dist/bar")</code> case.</p>
<p dir="auto"><strong>Open questions</strong></p>
<ul dir="auto">
<li>Use one typings dir for both 'wrapped' and 'unwrapped' typings?</li>
<li>First do a lookup for external modules already <code class="notranslate">declare</code>'ed, e.g. through tsd.d.ts? If so, would probably make first bullet usable, nice for gradual upgrade path of DT typings.</li>
<li>Most DT typings will not want to mimic exact filesystem structure of package, using e.g. <code class="notranslate">myutils/latest/index.d.ts</code> will probably work out-of-the-box with external module resolution logic, but <code class="notranslate">myutils/latest/myutils.d.ts</code> or even <code class="notranslate">myutils/myutils.d.ts</code> may be easier for filename in editor tab?</li>
</ul>
<p dir="auto">Good idea? Bad idea?</p>
<p dir="auto"><strong>TypeScript Version:</strong></p>
<p dir="auto">1.7.5 / 1.8.0-beta / nightly (1.9.0-dev.20160217)</p>
<p dir="auto"><strong>Code</strong></p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// A self-contained demonstration of the problem follows...
this._register('vscode.diff', (left: URI, right: URI, label: string) =&gt; {
    return this._commands.executeCommand('_workbench.diff', left, right, label);
}, {
    description: 'Opens the provided resources in the diff editor to compare their contents.',
    args: [
        { name: 'left', description: 'Uri of the resource for the left hand side of the diff editor', constraint: value =&gt; value instanceof URI },
        { name: 'right', description: 'Uri of the resource for the right hand side of the diff editor', constraint: value =&gt; value instanceof URI },
        { name: 'label', description: 'Label to use for the diff editor', constraint: value =&gt; typeof value === 'string' }
    ]
});"><pre class="notranslate"><span class="pl-c">// A self-contained demonstration of the problem follows...</span>
<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-en">_register</span><span class="pl-kos">(</span><span class="pl-s">'vscode.diff'</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-s1">left</span>: <span class="pl-smi">URI</span><span class="pl-kos">,</span> <span class="pl-s1">right</span>: <span class="pl-smi">URI</span><span class="pl-kos">,</span> <span class="pl-s1">label</span>: <span class="pl-smi">string</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">_commands</span><span class="pl-kos">.</span><span class="pl-en">executeCommand</span><span class="pl-kos">(</span><span class="pl-s">'_workbench.diff'</span><span class="pl-kos">,</span> <span class="pl-s1">left</span><span class="pl-kos">,</span> <span class="pl-s1">right</span><span class="pl-kos">,</span> <span class="pl-s1">label</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-kos">{</span>
    <span class="pl-c1">description</span>: <span class="pl-s">'Opens the provided resources in the diff editor to compare their contents.'</span><span class="pl-kos">,</span>
    <span class="pl-c1">args</span>: <span class="pl-kos">[</span>
        <span class="pl-kos">{</span> <span class="pl-c1">name</span>: <span class="pl-s">'left'</span><span class="pl-kos">,</span> <span class="pl-c1">description</span>: <span class="pl-s">'Uri of the resource for the left hand side of the diff editor'</span><span class="pl-kos">,</span> <span class="pl-en">constraint</span>: <span class="pl-s1">value</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">value</span> <span class="pl-k">instanceof</span> <span class="pl-smi">URI</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-kos">{</span> <span class="pl-c1">name</span>: <span class="pl-s">'right'</span><span class="pl-kos">,</span> <span class="pl-c1">description</span>: <span class="pl-s">'Uri of the resource for the right hand side of the diff editor'</span><span class="pl-kos">,</span> <span class="pl-en">constraint</span>: <span class="pl-s1">value</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">value</span> <span class="pl-k">instanceof</span> <span class="pl-smi">URI</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-kos">{</span> <span class="pl-c1">name</span>: <span class="pl-s">'label'</span><span class="pl-kos">,</span> <span class="pl-c1">description</span>: <span class="pl-s">'Label to use for the diff editor'</span><span class="pl-kos">,</span> <span class="pl-en">constraint</span>: <span class="pl-s1">value</span> <span class="pl-c1">=&gt;</span> <span class="pl-k">typeof</span> <span class="pl-s1">value</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-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto"><strong>Expected behavior:</strong></p>
<ul dir="auto">
<li>goto ts playground</li>
<li>paste the code and invoke format</li>
<li>you get this (note the wrong indent on the last 7 lines):</li>
</ul>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="this._register('vscode.diff', (left: URI, right: URI, label: string) =&gt; {
    return this._commands.executeCommand('_workbench.diff', left, right, label);
}, {
        description: 'Opens the provided resources in the diff editor to compare their contents.',
        args: [
            { name: 'left', description: 'Uri of the resource for the left hand side of the diff editor', constraint: value =&gt; value instanceof URI },
            { name: 'right', description: 'Uri of the resource for the right hand side of the diff editor', constraint: value =&gt; value instanceof URI },
            { name: 'label', description: 'Label to use for the diff editor', constraint: value =&gt; typeof value === 'string' }
        ]
    });"><pre class="notranslate"><code class="notranslate">this._register('vscode.diff', (left: URI, right: URI, label: string) =&gt; {
    return this._commands.executeCommand('_workbench.diff', left, right, label);
}, {
        description: 'Opens the provided resources in the diff editor to compare their contents.',
        args: [
            { name: 'left', description: 'Uri of the resource for the left hand side of the diff editor', constraint: value =&gt; value instanceof URI },
            { name: 'right', description: 'Uri of the resource for the right hand side of the diff editor', constraint: value =&gt; value instanceof URI },
            { name: 'label', description: 'Label to use for the diff editor', constraint: value =&gt; typeof value === 'string' }
        ]
    });
</code></pre></div>
<p dir="auto"><strong>Actual behavior:</strong></p>
<p dir="auto">indent properly</p>