Module vs Namespace - Import vs Require Typescript
TypescriptTypescript Problem Overview
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
module
is for external packagesnamespace
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][1], 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 [1]: https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html
Typescript Solutions
Solution 1 - Typescript
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);
})();
Solution 2 - Typescript
There are two things:
- A module in TypeScript is a standard ES6 notion, it uses
import
/export
keywords at the top level of the code; - A namespace is a notion specific to TypeScript to help to organize the code in an obsolete fashion.
Namespaces
It is almost an obsolete notion. Prior to ES6 modules, the common way to separate JavaScript code in a browser was to create global variables. For example, all the functions of an API like underscore was located in a global variable named _
.
This old way to proceed is like Java packages or PHP namespaces. It is not adapted to the Web. The new ECMAScript standard solves issues like: how to use two libraries that have the same name? How to use two distinct versions of the same library?
Notice: In versions of TypeScript prior to the ECMAScript definition of “modules” (summer 2014), namespaces were called “internal modules” and modules were called “external modules”.
Notice 2: The keyword export
inside a namespace
is a non-standard TypeScript usage of the keyword. It is the means to declare a thing that is publicly accessible from outside the namespace.
ES6 modules
A module is a file that contains keywords import
or export
at the top level of the code.
TypeScript follows the standard from ECMAScript. I suggest to read a good introduction to ES6 modules in an article from Mozilla.
If you want to use modules in a front-end application (in a browser), then you will have to use a bundler (Webpack [the documentation here], Browserify) or a loader (SystemJS [a tutorial here], RequireJS) and to configure TypeScript with this environment.
If your code is executed in Node.js, just configure the TypeScript compiler to generate the CommonJS format.
Notice: A namespace can be declared in a module. In that case, it won't be accessible as a global variable from outside the module. However, it can be exported from the module.
Solution 3 - Typescript
> 1. module is for external packages 2. namespace is for internal packages
Actually the module
keyword has been replaced with the namespace
keyword.
A better statement is thus Modules are what used to be called external modules, namespace is what used to be called internal modules.
More
Hope this helps futher : https://basarat.gitbooks.io/typescript/content/docs/project/modules.html
Solution 4 - Typescript
"require" and "import" are equivalent in functionality. we can use them both interchangeably because we have transpilers that don't really care whether the browser supports them natively or not. but, while "require" has its roots in old coding style coming from CommonJS back to 2009, "import" is deriving its syntax from the widely accepted ES6 (ES2015) syntax. so you should use "import" for new projects and not "require".
Solution 5 - Typescript
Naming Confusion
In the early days of Typescript, the namespaces were called internal modules and the ES6 Modules were called external modules.
Now for declaring the namespaces, the Typescript team recommends using the namespace { }
instead of the module { }
syntax to avoid the naming confusion with the external modules. Because the external modules are now simply 'modules' and internal modules are 'namespaces'.
Namespaces
Declaration
A namespace in Typescript can be declared using either the namespace
or the module
keyword. Both the keywords do the same thing. Then we can decide which part of our namespace to make public using the export
keyword.
// LivingThings.ts
export namespace Animals {
export class Dog { }
export class Cat { }
}
export namespace Plants {
export class Orchid { }
export class Bamboo { }
}
// LivingThingsUser.ts
import { Animals, Plants } from "./LivingThings"
Logical Grouping
Before ES6, the namespaces were used in Typescript for encapsulating the interfaces, classes, functions and variables to support a group of related functionalities and hide implementation details. This way we could prevent variables from leaking into the global space. This helped in better code organisation and prevent name collisions. Now it is recommended to use ES6 modules to achieve this.
The namespaces are now used for ambient namespace declarations.
Single File Usage
We can declare namespaces across multiple files and they can be concatenated using --outFile
flag. We can then use that concatenated file inside the <script>
tag in our HTML page. This allows us to structure our code in a good way in a client-side web application with all dependencies included.
Modules
Declaration
Modules are also called ES6 modules. We use multiple files for grouping the related functionalities and just use the export
keyword to make the desired object publicly visible.
// Animals.ts
export class Dog { }
export class Cat { }
// Plants.ts
export class Orchid { }
export class Bamboo { }
// LivingThingsUser.ts
import { Dog, Cat } from "./Animals"
import { Orchid, Bamboo } from "./Plants"
Logical Grouping
The logical grouping in modules is achieved by using separate files for grouping the related functionalities. For this reason, the external modules are also called file modules.
Single File Usage
We don't load the modules of the client-side web application using the <script>
tag, because the browsers may get sluggish while downloading so many files and rendering the page at the same time. For this, we use the module loaders like the CommonJS, AMD, SystemJS that enable us to load files asynchronously or concatenate the external module files into a single optimized file.
For server-side, especially in Node.js, the modules are strongly recommended.
That's it!