Stop TSC removing AMD dependencies

Topics: General
May 16, 2014 at 2:37 PM
Edited May 16, 2014 at 2:55 PM
I have the following:
import ChildModule = require('./ChildModule');

angular.module('app', ['myChildModule']);
I want RequireJS to load in my ChildModule script, which contains a definition for an Angular module called myChildModule. The problem is that it seems the TSC detects that the imported ChildModule isn't used in this script, so the resulting AMD module doesn't include this as a dependency. I get the following JS, which omits the expected './ChildModule' dependency:
define(["require", "exports"], function(require, exports) {
    angular.module('app', ['myChildModule']);
});
If I reference the ChildModule import in my app script with something like var a = ChildModule, then TSC keeps the AMD dependency and all is well. But this doesn't feel quite right.

Is there a better way to ensure I can ensure my script dependencies are loaded so Angular can use them?

Thanks.
May 16, 2014 at 5:03 PM
This is definitely a problem and I've had to fix it in a similar fashion in my project.

https://github.com/ShamnaSkor/WafleProject/blob/DurandalUI/Wafle/Wafle.WebUI/app/about.ts

There are a few bug reports already around this and also just generally the idea that TypeScript 1.0 does a bad job regarding separating the logical concept of external modules from the physical realities of how AMD modules actually work in folder structures on web servers:
https://typescript.codeplex.com/workitem/934
https://typescript.codeplex.com/workitem/911

Some members of the TypeScript team have expressed that they are thinking about how to make this better, but as of today there are still a lot of rough edges to work around with regards to external modules.
Marked as answer by MarcusWhit on 5/17/2014 at 2:27 AM
May 16, 2014 at 6:03 PM
Edited May 16, 2014 at 6:06 PM
Consider writing
angular.module('app', [ChildModule.name]);
instead of
angular.module('app', ['myChildModule']);
That's what Google's AngularJS Style Guide suggests.
May 17, 2014 at 9:32 AM
@thorn0 - this would necessitate adding the module to the global window scope, which isn't desirable. The google styleguide is also targeted towards those using Closure, which is the main reason why they recommend doing it in this way:
Why? Using a property of my.submoduleA prevents Closure presubmit failures complaining that the file is required but never used. Using the .name property avoids duplicating strings.
May 17, 2014 at 10:28 AM
Edited May 21, 2014 at 7:49 AM
MarcusWhit wrote:
@thorn0 - this would necessitate adding the module to the global window scope
No, it absolutely wouldn't. ChildModule is an AMD module that is required by some other module. What stops you from accessing its name property inside of this other module? Just use export = in ChildModule.ts.

ChildModule.ts:
var mod = angular.module('myChildModule', [])
    .config(...)
    .directive(...)
    // etc
export = mod;
(I wish we could leave out var mod and write just export = angular.module(...), but alas, it doesn't work. Created an issue about it.)

app.ts;
import ChildModule = require('./ChildModule');
angular.module('app', [ChildModule.name]);
compiled JS for ChildModule.ts:
define(["require", "exports"], function(require, exports) {
    var mod = angular.module('myChildModule', [])
        .config(...)
        .directive(...)
        // etc
    return mod;
});
compiled JS for app.ts:
define(["require", "exports", "./ChildModule"], function(require, exports, ChildModule) {
    angular.module('app', [ChildModule.name]);
});
Another thing you can use instead of imports is <amd-dependency> tags.