Nested npm modules with ambient typings files and duplicate identifiers

First off, wow, that title is a lot. There is probably a much more elegant and concise way to say that, but I didn't want to waste more brian cells on that, as I am running low, trying to solve what the title describes.

OK, so here is the scenario. Let's say you have a node module "A" that depends on "lodash" as a dependency. Let's also say that you wish to provide an ambient module declaration for node module "A". This all seems easy, especially if you are aware of the amazing TSD tools provided by the DefinitelyTyped regime.

Embedded ambient typing files

TSD supports a link comand that will search your node_modules directory for modules with a package.json that includes the ['typescript']['definition'] value. Here is an example.

{
    "typescript": {
        "definition": "./node-dependency.d.ts"
    }
}

Consumer

As mentioned above, the consumer side can consume this node module, if published, via NPM, and if not, via git+ssh... and run npm install. Then, the consumer just needs to run tsd link --save and TSD will add a reference to typings/tsd.d.ts to the node module typing definition, if declared in the package.json.

Pics or it didn't happen

OK, there is a lot of text here, but this isn't easy to follow. Enter the example...

References:

node-depencency is a git repository that represents a simple node module. It has an index.js file that simply maps the lodash object to exports.util.lodash. The example isn't important. What is important is the duplicate references, which I am getting to.

node-consumer is a git repository that has a dependency on node-dependency. It has an index.ts file that pulls in the node-dependency module.

OK, so the example is a little contrived, but here is the point. node-dependency has a hard reference on lodash. For the node-dependency.d.ts file to accurately reflect the objects that it exposes, it should import _ = require('lodash'); and make sure the exports.util.lodash file is of type _.LoDashStatic. The natural approach here is to include a reference at the top of node-depenency.d.ts for tsd.d.ts like /// <reference path="typings/tsd.d.ts" />. The problem with doing this is if a down-stream project the depends on node-dependency also depends on lodash. If it does, you will encounter a whole bunch of Duplicate identifier errors when compiling.

What is the solution?? The solution is to not use the xml reference comment of /// <reference path"typings/tsd.d.ts" /> in your ambient module declaration. Make sure that you still include the import statements like import _ = require('lodash'); as you normally would. By not including the xml reference, it will force down stream consumers to manage these typing references which is fine. If they do not, when they run the tsc compiler, they will get an error message appropriate with the error.

Working example

git clone git@github.com:adamcarr/node-consumer.git  
cd node-consumer  
npm install  
npm run build  

Broken example

Key difference here is node-consumer depends on the #incorrect branch of node-dependency which includes the xml typing reference comment and will cause the downstream tsc command to fail with duplicate identifiers.

git clone git@github.com:adamcarr/node-consumer.git  
cd node-consumer  
git fetch  
git checkout incorrect  
npm install  
npm run build  
comments powered by Disqus