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
2
3
4
// allValidator.ts
export * from './StringValidator'
export * from './LetterOnlyValidator'
export * from './ZipCodeValidator'

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
2
3
4
5
6
// jQuery.ts
declare let $: Jquery;
export default $;

// App.ts
import $ from 'jQuery'

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
2
declare var mynumber: any;
mynumber = 300; // ok

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
2
3
function showMessage (message) {
alert(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
2
3
declare module Something {
var x;
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// node.d.ts
declare module 'url' {
export interface Url {
protocol?: string;
hostname?: string;
pathname?: string;
}

export function parse (urlStr: string, parseQueryString?, slashesDenotateHost?): Url
}

declare module 'path' {
export function normalize(p: string): string;
export function join(...paths: any[]): string;
export var sep: string;
}

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
2
3
/// <reference path='node.d.ts' />
import * as URL from 'url'
let myUrl = URL.parse('http://www.typescriptlang.org')

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
2
import * as $ from 'jquery'
import { Component } from '@angular/core'

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 { ... }(remove Foo and move everything ‘up’ a level.)

  • A file that has a single export class or export function(consider using export 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
2
3
4
5
6
7
8
9
10
// extendedNamespace_part2.d.ts
namespace ArrayUtilities {
// ...
}

// extendedNamespace_part1.d.ts
/// <reference path="extendedNamespace_part2.d.t.s" />
namespace ArrayUtilities {
// ...
}