17

I'm a beginner at using js modules.

I'm working on a fairly simple web application. It uses typescript and angular 2, which heavily relies on modules.

Most of my app ts files 'import' one or many js modules (usually mostly angular 2 modules).

As I understand, because my app ts files have a top level 'import', they are automatically considered a js module by typescript.

However, I want any of my app ts files to be accessible by any other of my app ts files, without having to 'import' each other. But because they are now modules themselves, ts requires me to do that...

Is it possible?

It seems crazy to me that for each of my app ts file, I should have to declare every other of my app ts files that are used in there (I like to have tiny files with a single class/interface). In addition, this relies on relative paths which breaks as soon as I restructure my folder structure.

Am I thinking about this the wrong way?

Clement
  • 3,990
  • 4
  • 43
  • 44
  • This form of dependency injection is usually intended to restrict access (in order to make code more modular) to code that is not required in a given module--instead requiring you to explicitly declare dependencies. If all of your modules have access to every other module you're using, it sounds like you might want to reconsider the way you're designing your application, but maybe I just don't understand correctly. – kondrak Sep 08 '16 at 04:53

4 Answers4

8

You must have a js file which is an entry point to your application right?.. So in that file just import all the modules which you want to access without importing and attach them to the window object. Since the window object is available globally, you can access your module from anywhere without importing the corresponding module. For example,

Consider this scenario: You have a module in a file called module1.ts The entry point of your application is a file called index.ts And you have a module2 where you require something from module1

// module1.ts
function add(first: number, second: number): number {
    return first + second
}
export {add}

in your index.ts

// index.ts
import {add} from '<path to module1>/module1';

window.add = add

Now in your module2

// module2.ts
window.add(1, 2)

Since the window object is available globally you can attach as many properties to it as you like. As far as the type resolution is concerned you can declare a window module with the add function you require in a .d.ts file as follows:

declare module window {
add: (first: number, second: number) => number 
}
Nahush Farkande
  • 5,290
  • 3
  • 25
  • 35
  • What do you mean? How so? – Clement Sep 08 '16 at 10:25
  • Updated the answer.. Hope it helps :) – Nahush Farkande Sep 08 '16 at 12:38
  • I don't think this will work:in the index.ts there is a top level import which means the file is considered a module, which means there is no window object. – Clement Sep 08 '16 at 22:37
  • As long as your code gets executed in the browser, you will have access to the `window` object in all your javascript files. In your case, in all your `ts` files. If the window object is not available then there has to be some way to inject it into your module. You can refer [this](http://stackoverflow.com/questions/34177221/angular2-how-to-inject-window-into-an-angular2-service) if you'd like to investigate how to inject the window object. As far as my understanding goes as long as the code gets executed in a browser all you modules should have access to the window object – Nahush Farkande Sep 09 '16 at 04:30
  • 1
    I searched and searched and this answer finally helped me. I want to use module-style library and non-module style libraries in my script. The non-module one relies on a few specifically named functions to be declared in your script (in what i assume is the "global" namespace). Using "import" made me have to "export" those functions, but they couldn't be seen by the non-module library. None of the blogs and junk had useful info. This answer reminded me that the "global" stuff is added to the window in the browser, so I had to manually add the necessary functions to the window object. THANKS! – Benny Jobigan Jun 26 '18 at 09:47
  • This worked for me, I am converting section of my web page to VueJS progressively so i had to keep a lot of my old non-module JS and the only way it worked to link both was this, meybe it is less secure considering exposing variables to be global but it will be like that untli the whole website runs on VueJS. – Mousa Alfhaily Jul 26 '20 at 12:13
4

Declaring dependencies (e.g modules) for each file is a double-edged sword.

The advantage is that there is no 'magic' - you know exactly where each function, variable, class etc. is coming from. This makes it much easier to know what libraries / frameworks are being used and where to look to troubleshoot issues. Compare it to opposite approach that Ruby on Rails uses with Ruby Gems, where nothing is declared and everything is auto-loaded. From personal experience I know it becomes an absolute pain to try to workout where some_random_method is coming from and also what methods / classes I have access to.

You're right that the disadvantage is that it can become quite verbose with multiple imports and moving relative files. Modern editors and IDEs like WebStorm and Visual Studio Code have tools to automatically update the relative paths when you move a file and also automatically add the imports when you reference code in another module.

One practical solution for multiple imports is to make your own 'group' import file. Say you have a whole bunch of utility functions that you use in all your files - you can import them all into a single file and then just reference that file everywhere else:

//File: helpers/string-helpers.ts

import {toUppercase} from "./uppercase-helper";
import {truncate} from "./truncate-helper";

export const toUppercase = toUppercase;
export const truncate =  truncate;

Then in any other file:

import * as StringHelpers from "../path-to/helpers/string-helpers";
...
let shoutingMessage = StringHelpers.toUppercase(message);

The disadvantage of this is that it may break tree shaking, where tools such as webpack remove unused code.

Pete
  • 2,196
  • 1
  • 17
  • 25
  • Right. I think you have a good point for pure js. Not relevant with ts though because the language understands where objects 'come from' so you can easily 'go to definition'... – Clement Sep 08 '16 at 10:29
  • I like your work around to re export common objects into a 'group' module. Quite practical! I think I'll go with that. Tree shaking should not be a pb in my case since I know I use all of my own code (not dead code). – Clement Sep 08 '16 at 10:31
0

Is it possible

Not in any easy way. The ts file is a module and uses e.g. module.exports (if commonjs) that will need to be shimmed out. And that is just the runtime story. The TypeScript story will be harder and one way would be to make a .d.ts file for the module stating the contents as global.

Like I said. Not worth doing. Modules are the way forward instead of making something hacky.

basarat
  • 261,912
  • 58
  • 460
  • 511
-1

It's not crazy at all. You are definitively thinking in the wrong way.
Actually what you don't like it's a common feature in all modern programming languages and it makes the code and structure of the app a lot clearer and simple to understand.

Without imports and going to old school way looks very crazy to me :)
You can have only chaos with so many global variables.

Tiberiu Popescu
  • 4,486
  • 2
  • 26
  • 38
  • Hmm yes at runtime everything is 'global' but at compile time typescript allows for private members so i still get some encaspulation of my classes... – Clement Sep 08 '16 at 10:22
  • 1
    I guess if you dont use typescript then i see why modules make more sense but the relative path thing still seems very brittle and encourages me to create much bigger files.... – Clement Sep 08 '16 at 10:24