Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Module vs Namespace - Import vs Require Typescript

Tags:

typescript

I am getting lot of confusion with module/namespace/export and import, require, reference usage. Being from Java background, Can someone explain me in nutshell when to use what and what's the right design? I feel I am messing up when I am writing sample project

So far this is my understanding 1. module is for external packages 2. namespace is for internal packages

  • I didn't get how we categorize them?
  • When to export a class or namespace or package?
  • If we export package/namespace, all classes within that are exported or need to be explicitly exported
  • How each one of them can be imported/required?

According to doc, if I am creating each "ts" file for each manager/model, Typescript doesn't recommend using "namespaces"? Directly use reference paths?

Please explain in detail as I am coming from different background and not sure about ES6/ES5 etc`.

I have seen several people raising/getting confused with same questions. I hope someone can explain in detail with real world scenario

like image 531
RaceBase Avatar asked Jul 26 '16 06:07

RaceBase


People also ask

What is the difference between namespace and module in TypeScript?

A module is a way which is used to organize the code in separate files and can execute in their local scope, not in the global scope. A namespace is a way which is used for logical grouping of functionalities with local scoping.

Should I use TypeScript namespace?

Don't use Custom TypeScript Modules and Namespaces Since we have ES6 modules as a standard in JavaScript, we don't need custom TypeScript modules and namespaces to organize our code. Instead, we should use standard JavaScript modules with import and export instead.

What is a namespace TypeScript?

The namespace is used for logical grouping of functionalities. A namespace can include interfaces, classes, functions and variables to support a single or a group of related functionalities. A namespace can be created using the namespace keyword followed by the namespace name.

Can we use require in TypeScript?

TypeScript offers support for creating and using modules with a unified syntax that is similar to the ES Module syntax, while allowing the developer to output code that targets different module loaders, like Node. js (CommonJS), require.


1 Answers

I didn't get how we categorize them?

Namespaces are used to organize/encapsulate your code. External modules are used to organize/encapsulate your code AND to locate your code at runtime. In practice, you have two choices at runtime: 1) combine all transpiled code into one file, or 2) use external modules and have multiple files and require some other mechanism to get at those files.

When to export a class or namespace or package?

To make a type or value visible outside of the file that it's in, you have to export it if it's inside of a namespace. Whether you export it at the top level or within a namespace will decide if it's now in an external module.

If we export package/namespace, all classes within that are exported or need to be explicitly exported

Classes in a namespace will always need to be explicitly exported for the class to be visible at compile time outside of the file in which it is defined.

How each one of them can be imported/required?

This depends of if you're using external modules. An external module will always need to be imported to "use" it. Importing a namespace that's not in an external module is really just providing an alias for the namespace -- you still have to prefix the type/whatever with the alias (and this is why you generally don't want to use namespaces with external modules; doing so means you always have to use a prefix when referencing anything provided by the external module.) Namespaces that aren't in an external module can span files, so if you're in the same namespace you can refer to anything exported by the namespace without needing any sort of import.

To really understand the above you need some background knowledge. The key thing to understand with references/namespaces/external modules is what these constructs do at compile time and what they do at runtime.

Reference directives are used at compile time to locate type information. Your source has a particular symbol in it. How does the TypeScript compiler locate the definition for that symbol? The reference directive has largely been subsumed by the tsconfig.json mechanism -- using tsconfig.json, you tell the compiler where all your sources are.

Namespaces can contain type definitions and/or implementation. If a namespace contain only type information then it has no runtime manifestation at all -- you can check this by looking at the JS output and finding an empty JS file. If a namespace has implementation code, then the code is wrapped inside a closure that is assigned to a global variable with the same name as the namespace. With nested namespaces, there will be a global variable for the root name space. Again, check the JS output. Namespaces are historically how JS client-side libraries have attempted to avoid the issue with naming collisions. The idea is to wrap your entire library into one closure and then expose as small a global footprint as possible -- just one global variable referencing the closure. Well, the problem is still that you've claimed a name in the global space. What if you wanted, say, two versions of a library? A TypeScript namespace still has the issue of how to locate the source for the namespace. That is, source code that references A.B still has the problem of telling the compiler how to locate A.B -- either by using reference directives or by using tsconfig.json. Or by putting the namespace into an external module and then importing the external module.

External modules originated with server-side JS. There is a one-to-one correspondence between an external module and a file on the file system. You can use the file system directory structure to organize external modules into a nested structure. Importing an external module will generally aways introduce a runtime dependency on that external module (the exception is when you import an external module but then don't use any of its exports in the value position -- that is, you only import the external module to get at its type information). An external module is implicitly in a closure, and this is key: the user of the module can assign the closure to whatever local variable they want. TypeScript/ES6 adds additional syntax around mapping the exports of the external modules to local names, but this is just a nicety. On the server side, locating an external module is relatively straight forward: just locate the file representing the external module on the local file system. If you want to use external modules on the client side, in a browser, it gets more complex as there's no equivalent to the file system that has the module available for loading. So now on the client side you need a way to bundle all those files into a form that can be used remotely in the browser -- this is where module bundlers like Webpack (Webpack does a heck of a lot more than bundle modules though) and Browserify come into play. Module bundlers allow runtime resolution of your external modules in the browser.

Real world scenario: AngularJS. Pretend external modules don't exist, use a single namespace to limit pollution of global space (in the example below a single variable MyApp is all that is in the global space), export only interfaces, and use AngularJS dependency-injection to make implementations available for use. Put all classes in a directory root, add a tsconfig.json to the root, install angularjs typings under the same directory root so that tsconfig.json picks it up too, combine all output int one JS file. This will work fine for most projects if code-reuse isn't much of a concern.

MyService.ts:

namespace MyApp {      // without an export the interface is not visible outside of MyService.ts     export interface MyService {          ....     }      // class is not exported; AngularJS DI will wire up the implementation     class MyServiceImpl implements MyService {     }      angular.module("MyApp").service("myService", MyServiceImpl); } 

MyController.ts:

namespace MyApp {     class MyController {        // No import of MyService is needed as we are spanning         // one namespace with multiple files.        // MyService is only used at compile time for type checking.         // AngularJS DI is done on the name of the variable.         constructor(private myService: MyService) {         }    }    angular.module("MyApp").controller("myController", MyController); } 

Using IIFE to avoid polluting global runtime scope. In this example, no global variables are created at all. (A tsconfig.json is assumed.)

Foo.ts:

namespace Foo {     // without an export IFoo is not visible. No JS is generated here     // as we are only defining a type.     export interface IFoo {         x: string;     } }  interface ITopLevel {     z: string; }  (function(){     // export required above to make IFoo visible as we are not in the Foo namespace     class Foo1 implements Foo.IFoo {         x: string = "abc";     }     // do something with Foo1 like register it with a DI system })(); 

Bar.ts:

// alias import; no external module created import IFoo = Foo.IFoo;  (function() {      // Namespace Foo is always visible as it was defined at     // top level (outside of any other namespace).     class Bar1 implements Foo.IFoo {         x: string;     }      // equivalent to above     class Bar2 implements IFoo {         x: string;     }      // IToplevel is visible here for the same reason namespace Foo is visible     class MyToplevel implements ITopLevel {         z: string;     }  })(); 

Using IIFE you can make eliminate introducing MyApp as a global variable in the first example.

MyService.ts:

interface MyService {      .... }  (function() {      class MyServiceImpl implements MyService {     }      angular.module("MyApp").service("myService", MyServiceImpl); })(); 

MyController.ts:

(function() {      class MyController {         constructor(private myService: MyService) {         }    }     angular.module("MyApp").controller("myController", MyController); })(); 
like image 67
kayjtea Avatar answered Oct 10 '22 05:10

kayjtea