Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using external modules in TypeScript

Tags:

typescript

Lets say we have the following 2 external modules in TypeScript:

export module My.Services.Common 
{
    export class Helper 
    {
       //...
    }
}

and

export module My.Services 
{
    export class Connection
    {
        //...
    }    
}

Now in my app.ts I would like to use both Connection and Helper classes. What I want to achieve is similar to the following code in C#:

using My.Services;
using My.Services.Common;

Or at least just

using My.Services;

But it looks like I cant work at the same time with both Helper and Connection. If I write:

import {My} from './services/common/Helper';
import {My} from './services/Connection;

Leads to error "duplicate identifier 'My'". That is logical though. So my question is how can I consume different classes from the same (or nested) modules?

like image 320
Amid Avatar asked May 11 '15 19:05

Amid


3 Answers

My personal view on this is to break away from what you see in C# or Java (which maps more closely to internal modules) and treat it more like the external modules you are using...

Step 1. Ditch the module keyword. The file is a module already.

Step 2. Provide a non-dotted alias when you import.

Step 3. You can now import everything, '*', or specific classes when you import.

./services/common.ts

export class Helper 
{
   //...
}

./services.ts

export class Connection
{
    //...
}

./app.ts

import * as Services from './services'
import { Connection } from './services/common'

var x = new Services.Helper();

var y = new Connection();

You can import multiple members from a module too:

import { Connection, Example } from './services/common'

var y = new Connection();

var z = new Example();
like image 105
Fenton Avatar answered Oct 12 '22 17:10

Fenton


To add to Steve's good answer, since I've already drawn the ASCII art: The top-level object exported from this module, My, doesn't merge with any other object from other files named My. The only thing the My.Services.Common module achieves is making it more annoying to import Helper, because you have to fully qualify its name even though there's nothing else in there.

What you think you've done:

/-My--------------------\
| /-Services---------\  |
| | /-Common---\     |  |
| | | [Helper] |     |  |
| | \----------/     |  |
| |                  |  |
| | [Connection]     |  |
| \------------------/  |
\-----------------------/

What you've actually done:

/-My---------------\   /-My---------------\ 
| /-Services-----\ |   | /-Services-----\ |
| | /-Common---\ | |   | | [Connection] | |
| | | [Helper] | | |   | \--------------/ |
| | \----------/ | |   \------------------/
| \--------------/ | 
\------------------/

This is like having an organization system in your house where you have a dozen shoeboxes, each with a single smaller box inside it, each with a smaller box inside that one. The nested boxes provide no organizational benefit over the shoeboxes!

like image 43
Ryan Cavanaugh Avatar answered Oct 12 '22 18:10

Ryan Cavanaugh


I have found another approach for tackling this organisation problem.

To go back to ASCII art (ᵔᴥᵔ)

My folder structure is so:

|
|-- myawesomemodule
|         |
|         |-- myawesomeclass.ts
|         |-- myevenmoreawesomeclass.ts
|         |-- myawesomemodule.ts
|
|-- myevenmoreawesomemodule
|         |-- myotherclass.ts
|         |-- myanotherclass.ts
|         |-- myevenmoreawesomemodule.ts
|
index.ts

In myawesomemodule.ts, I do:

export {MyAwesomeClass} from './myawesomeclass'
export {MyEvenMoreAwesomeClass} from './myevenmoreawesomeclass'

Similarly in myevenmoreawesomemodule.ts, I do:

export {MyOtherClass} from './myotherclass'
export {MyAnotherClass} from './myanotherclass'

And finally at the root level in index.ts, I do:

import * as MyAwesomeModule from "./myawesomemodule/myawesomemodule";
import * as MyEvenMoreAwesomeModule from "./myevenmoreawesomemodule/myevenmoreawesomemodule";

export {MyAwesomeModule};
export {MyEvenMoreAwesomeModule};

Now I can ship this module off to a consumer, pack as NPM etc.

From a consumer, I do:

import MyPackage = require("mypackage"); //assuming you did npm pack 

OR

import * as MyPackage from "./path/to/index" //assuming you didn't

Then I refer to my class names so:

let instance = new MyPackage.MyAwesomeModule.MyAwesomeClass();

I find this approach a bit better than exposing all my classes in a big module at the root level.

like image 25
Varun Chatterji Avatar answered Oct 12 '22 17:10

Varun Chatterji