Modules
Modules are used for code organization as well as code sharing.
Modules are executed within their own scope, not in the global scope. This means that variables, functions, classes, etc. declared in a module are not visible outside the module unless they are explicitly exported using export
. Conversely, to consume a variable, function, class, interface, etc. exported from a different module, it has to be imported using import
.
Modules are declarative.
In TypeScript any file containing a top-level import
or export
is considered a module.
Export
Exporting a declaration.
Any declaration(such as variable, function, class, type alias, or interface) can be exported by adding the export
keyword.
A module can wrap one or more modules and combine all their exports using export * from
syntax.
1 | // allValidator.ts |
Import can be renamed.
1 | import { ZipCodeValidator as ZCV } from './ZipCodeValidator' |
Default exports
Each module can optionally export a default
export. Default exports are marked with the keyword default
, and there can only be one default
export per module.
1 | // jQuery.ts |
Declaration Files
Declarations are used to describe code that exists elsewhere using the declare
keyword. The goad of this is to be able to use this code in TypeScript applications without having to rewrite the code in TypeScript.
1 | declare var mynumber: any; |
You can save these declaration in a .ts
file or in a .d.ts
file, we call these file with .d.ts
extension declaration files.
It’s good practice to keep your declarations in separate .d.ts
file.
If a file has the extension .d.ts
then each root level definition must have the declare
keyword prefixed to it. This tells the developer that there will be no code emitted by TypeScript. The developer needs to ensure that the declared item will exist at runtime.
For example, lets assume we have the following JavaScript code(message.js)
1 | function showMessage (message) { |
If you want to call the showMessage
function in your TypeScript code, TypeScript will not recognize that this function exists. It does not know its name or its parameter. This can be fixed by describing the showMessage
function in a definition file as (message.d.ts):
1 | declare function showMessage(message: string) |
Now you can use the function showMessage
in TypeScript without compile errors.
.d.ts files and .ts files
Anything allowed in a .d.ts
file mayu also appear in a .ts
file, but not the reverse. Therefore, .d.ts
allows a subset of TypeScript features. A .d.ts
file is only allowed to contain TypeScript code that doesn’t generate any JavaSAcript code in the output. If you attempt to use any feature of TypeScript that would generate JavaScript, you’ll get an error.
Interfaces are allowed, because they disappear completely after compilation.
Const enums are also allowed, unlike ordinary enums which generate an object in the output JavaScript.
Top level classes, variables, modules, and functions must be prefixed with declare
.
It is a common practice to see a top-lvel declare module
and then all definition inside it are therefore also declaraion.
1 | declare module Something { |
Working with other JavaScript Libraries.
To describe the shape of libraries not written in TypeScript, we need to declare the API that the library exposes.
We call declarations that don’t define an implementation ‘ambient’. Typically these are defined in .d.ts
files.
Ambient Modules
We can define each module in its own .d.ts
file with top-level export declarations, but it’s more convenient to write them as one large .d.ts
file. To do so, we use a construct similar to ambient namespaces, but we use the module
keyword and the quoted name of the module which will be available to a later import.
1 | // node.d.ts |
Now we can use /// <reference>node.d.ts
and then load the modules using import url = require('url')
or import * as URL from 'url'
1 | /// <reference path='node.d.ts' /> |
Module Resolution
Relative vs Non-relative module imports
Module imports are resolved differently based on whether the module reference is relative or non-relative.
A relative import is one that starts with /, ./, ../
.
Any other import is considered non-relative.
1 | import * as $ from 'jquery' |
A relative import is resolved relative to the importing file and cannot resolve to an ambient module declaration.
A non-relative import can be resolved relative to baseUrl
, or through path mapping. They can also resolve to ambient module declaations.
Module Resolution Strategies
There are two possible module resolution strategies: Node
and Classic
.
You can use the --moduleResolution
flag to specify the module resolution strategy. If not specified, the default is Classic
for --module AMD | System | ES2015 | Node
.
Modules or Namespaces
Namespaces vs Modules
If fact, namespaces were previously refered to as internal modules while modules where refered to as external modules.
When to use namespaces and when to use moduels
When first moving to a module-based organization, a common tendency is to wrap exports in an additional layer of namespaces. Modules have their own scope, and only exported declaration are visible from outside the module. With this in mind, namespaces provide very little, if any, value when working with modules.
On the oraganization front, namespaces are handy for grouping together logically-related objects and types in the global.
Namespaces are important to avoid naming collisions in the scope.
Reg Flags
Dont’t use namespace in modules
All of the following are reg flags for module structuring.
A file whose only top-level declaration is
export namespace Foo { ... }
(removeFoo
and move everything ‘up’ a level.)A file that has a single
export class
orexport function
(consider usingexport default
)Multiple files that have the same
export namespace Foo
at top-level(don’t think that these are going to combine into one).
Best Practice
While
namespaces
are still available in TypeScript, it is better to use modules as much as possible. This is because in most cases you will need to use external libraries in your application. In that case you will have to deal with modules because modules are the way to go with external none TypeScript libraries. Mixing namespaces and modules in one application is not a recommended practice.Use modules and not namespaces if you are going to share your code as components or libraries to be consumed by other applications.
Use namespaces if your application is small and within the same application and when you do have any external dependencies like using external libraries or offering your code to be used by other application. You can still use modules in this case.
Working with External Libraries
Importing a JavaScript Library.
There are two options to bring a javascript library in your typescrpt project:
Manual download: this is by manually copying some definitions files for the obejcts used in the JavaScript Libraries into your TypeScript Project so that TypeScript can recognize and be able to support the typings. There is a big effort form the TypeScript community to provide those declaration files for almost all the third party JavaScript Libraries out there for developers to copy and include in their projects without having to rewrite the types themselves. This project is called the definitely typed project.
Using TyepSearch: This will automatically install the definition files for the imported libraries. The TypeSearch project that is a Microsoft initiative that uses npm to install lthe library definition files automatically with no need to manually copy any files.
Combine two namespaces with same identifier
1 | // extendedNamespace_part2.d.ts |