Reputation: 1068
I can't figure out if it even possible to have an "export module" spread accross multiple files.
If I have file Contact.ts:
// file Contact.ts
export module Contacts {
export class Contact {
...
}
}
and another ContactView.ts
// file ContactView.ts
export module Contacts {
export class ContactView {
model: Contact; // <--- is not recognized
}
}
Then TSC is not recognizing the Contact class. As you can see the Contact and the ContactView are declared to reside in the same module and according to the spec it should work.
I'm building a composite app that uses the require.js and AMD patterns so I HAVE to use the "export module" declaration.
Should I do some type of "ahead declaration" or some tricky "import" ?
Thanks for the advise.
EDIT: Currently I load each module separately via import, but, if you'll notice, it creates an enormous waste of code and lot of "import" dependencies. My question was if there is a way to use the same namespace (i.e. Contacts) to let know the TS that I do not mean to import. I was looking into the normal // command, but it doesn't work. I even tried the *.d.ts declaration files with no success so far.
Upvotes: 8
Views: 5428
Reputation: 9474
I struggled with the same question for a while, and just wanted to share what I am doing in case anyone else wanders across this question.
First, I defined myself a reference file that declares all of the files in my module:
/// <reference path="_contacts.dependencies.ts" />
/// <reference path="../contacts/Contact.ts" />
/// <reference path="../contacts/ContactView.ts" />
/// <reference path="../contacts/ContactModel.ts" />
Note that paths specified inside the file are relative to the location of the reference file itself (_contacts.ts
), unlike a .js
reference file. My directory structure looks like this:
modules
references // all of the reference files
knockout
underscore
// ... a subfolder for every 3rd party library used
contacts
commerce
// ... other modules at same level as contacts
Back to the reference file itself. The first line includes a separate reference file listing all of the external libraries used by the module, such as underscore, moment or any other existing library you have a .d.ts
definition file for. The remaining lines are the files that make up the module.
Inside every file that is part of the module, I reference the above file:
/// <reference path="../references/_contacts.ts" />
module Contacts {
export class Contact {
public model: ContactModel;
// ...
}
}
Similarly, you can create a single reference file to list all your modules:
/// <reference path="_address.ts" />
/// <reference path="_contacts.ts" />
/// <reference path="_commerce.ts" />
And simply point to this from your source files.
This does not solve the problem of the emitted code being in separate files, though. For that problem I am using a JavaScript minification tool, which is capable of bundling up multiple files into a single source file. Depending on your compilation settings and use case needs, you may need to apply some wrapper around the generated code for it to work as an AMD module (not too familiar with that part yet).
Upvotes: 4
Reputation: 4315
The spec allows you to define internal modules across multiple files (in essence, internal modules refer to the javascript module pattern). External modules, such as AMD or CommonJS modules, work on the idea that each file is the actual "module of code", and the namespacing/naming within it is irrelevant since the module will be loaded into its own new object anyway.
You could write the following code to load the Contact.ts module inside of the ContactView.ts module:
// file ContactView.ts
import mod = module("./Contact");
export module Contacts {
export class ContactView {
model: mod.Contacts.Contact; // <--- will be recognized
}
}
And that should work well enough, but if you wanted to have access to the contents of both modules in another area (to for example make a new Contact model yourself), you would have to essentially import both of them:
import c = module("./Contact");
import cv = module("./ContactView");
Which I think is fine enough, because you're clearly stating your dependencies. The downside is that they wont share a common parent object, so having them both be in a "Contact" module-pattern probably isn't of great use.
Another option is to export "Contact" along with "ContactView", as follows (granted this code is kind of silly because you're already doing exactly that via the model property of ContactView, but never the less...):
export module Contacts {
export class ContactView {
model: mod.Contacts.Contact;
constructor() {
this.model = new mod.Contacts.Contact();
}
}
export var Contact = mod.Contacts.Contact;
}
So you'd be able to access it after having loaded ContactView.
EDIT: By the way, you aren't limited to only exporting modules via "export module Name { ... }", you can export anything as the file itself is the module. So you could have a file which just has "export function foo() { ... }" without any module-pattern code wrapping it.
EDIT2: It looks like AMD might have functionality for loading multiple dependencies and constructing "modules" from those, but I have no idea how that would work in TS, here's a link that goes over that: http://www.adobe.com/devnet/html5/articles/javascript-architecture-requirejs-dependency-management.html (Constructor modules).
Upvotes: 6