11.3 Modules

Source files that contain at least one module import or export declaration are considered separate modules. Non-exported entities declared in a module are in scope only in that module, but exported entities can be imported into other modules using import declarations.

  ImplementationModule:   ImplementationModuleElementsopt

  ImplementationModuleElements:   ImplementationModuleElement   ImplementationModuleElementsImplementationModuleElement

  ImplementationModuleElement:   ImplementationElement   ImportDeclaration   ImportAliasDeclaration   ImportRequireDeclaration   ExportImplementationElement   ExportDefaultImplementationElement   ExportListDeclaration   ExportAssignment

  DeclarationModule:   DeclarationModuleElementsopt

  DeclarationModuleElements:   DeclarationModuleElement   DeclarationModuleElementsDeclarationModuleElement

  DeclarationModuleElement:   DeclarationElement   ImportDeclaration   ImportAliasDeclaration   ExportDeclarationElement   ExportDefaultDeclarationElement   ExportListDeclaration   ExportAssignment

Initialization order of modules is determined by the module loader being used and is not specified by the TypeScript language. However, it is generally the case that non-circularly dependent modules are automatically loaded and initialized in the correct order.

Modules can additionally be declared using AmbientModuleDeclarations in declaration scripts that directly specify the module names as string literals. This is described further in section 12.2.

Below is an example of two modules written in separate source files:

  1. // -------- main.ts --------
  2. import { message } from "./log";
  3. message("hello");
  4. // -------- log.ts --------
  5. export function message(s: string) {
  6. console.log(s);
  7. }

The import declaration in the ‘main’ module references the ‘log’ module and compiling the ‘main.ts’ file causes the ‘log.ts’ file to also be compiled as part of the program.

TypeScript supports multiple patterns of JavaScript code generation for modules:

  • CommonJS. This format is used by server frameworks such as node.js.
  • AMD (Asynchronous Module Definition). This format is used by asynchronous module loaders such as RequireJS.
  • UMD (Universal Module Definition). A variation of the AMD format that allows modules to also be loaded by CommonJS loaders.
  • System. This format is used to represent ECMAScript 2015 semantics with high fidelity in down-level environments.

The desired module code generation pattern is selected through a compiler option and does not affect the TypeScript source code. Indeed, it is possible to author modules that can be compiled for use both on the server side (e.g. using node.js) and on the client side (using an AMD compliant loader) with no changes to the TypeScript source code.

11.3.1 Module Names

Modules are identified and referenced using module names. The following definition is aligned with that provided in the CommonJS Modules 1.0 specification.

  • A module name is a string of terms delimited by forward slashes.
  • Module names may not have file-name extensions like “.js”.
  • Module names may be relative or top-level. A module name is relative if the first term is “.” or “..”.
  • Top-level names are resolved off the conceptual module name space root.
  • Relative names are resolved relative to the name of the module in which they occur.

For purposes of resolving module references, TypeScript associates a file path with every module. The file path is simply the path of the module’s source file without the file extension. For example, a module contained in the source file ‘C:\src\lib\io.ts’ has the file path ‘C:/src/lib/io’ and a module contained in the source file ‘C:\src\ui\editor.d.ts’ has the file path ‘C:/src/ui/editor’.

A module name in an import declaration is resolved as follows:

  • If the import declaration specifies a relative module name, the name is resolved relative to the directory of the referencing module’s file path. The program must contain a module with the resulting file path or otherwise an error occurs. For example, in a module with the file path ‘C:/src/ui/main’, the module names ‘./editor’ and ‘../lib/io’ reference modules with the file paths ‘C:/src/ui/editor’ and ‘C:/src/lib/io’.
  • If the import declaration specifies a top-level module name and the program contains an AmbientModuleDeclaration (section 12.2) with a string literal that specifies that exact name, then the import declaration references that ambient module.
  • If the import declaration specifies a top-level module name and the program contains no AmbientModuleDeclaration (section 12.2) with a string literal that specifies that exact name, the name is resolved in a host dependent manner (for example by considering the name relative to a module name space root). If a matching module cannot be found an error occurs.

11.3.2 Import Declarations

Import declarations are used to import entities from other modules and provide bindings for them in the current module.

An import declaration of the form

  1. import * as m from "mod";

imports the module with the given name and creates a local binding for the module itself. The local binding is classified as a value (representing the module instance) and a namespace (representing a container of types and namespaces).

An import declaration of the form

  1. import { x, y, z } from "mod";

imports a given module and creates local bindings for a specified list of exported members of the module. The specified names must each reference an entity in the export member set (11.3.4.4) of the given module. The local bindings have the same names and classifications as the entities they represent unless as clauses are used to that specify different local names:

  1. import { x as a, y as b } from "mod";

An import declaration of the form

  1. import d from "mod";

is exactly equivalent to the import declaration

  1. import { default as d } from "mod";

An import declaration of the form

  1. import "mod";

imports the given module without creating any local bindings (this is useful only if the imported module has side effects).

11.3.3 Import Require Declarations

Import require declarations exist for backward compatibility with earlier versions of TypeScript.

  ImportRequireDeclaration:   importBindingIdentifier=require(StringLiteral);

An import require declaration introduces a local identifier that references a given module. The string literal specified in an import require declaration is interpreted as a module name (section 11.3.1). The local identifier introduced by the declaration becomes an alias for, and is classified exactly like, the entity exported from the referenced module. Specifically, if the referenced module contains no export assignment the identifier is classified as a value and a namespace, and if the referenced module contains an export assignment the identifier is classified exactly like the entity named in the export assignment.

An import require declaration of the form

  1. import m = require("mod");

is equivalent to the ECMAScript 2015 import declaration

  1. import * as m from "mod";

provided the referenced module contains no export assignment.

11.3.4 Export Declarations

An export declaration declares one or more exported module members. The exported members of a module can be imported in other modules using import declarations (11.3.2).

11.3.4.1 Export Modifiers

In the body of a module, a declaration can export the declared entity by including an export modifier.

  ExportImplementationElement:   exportVariableStatement   exportLexicalDeclaration   exportFunctionDeclaration   exportGeneratorDeclaration   exportClassDeclaration   exportInterfaceDeclaration   exportTypeAliasDeclaration   exportEnumDeclaration   exportNamespaceDeclaration   exportAmbientDeclaration   exportImportAliasDeclaration

  ExportDeclarationElement:   exportInterfaceDeclaration   exportTypeAliasDeclaration   exportAmbientDeclaration   exportImportAliasDeclaration

In addition to introducing a name in the local declaration space of the module, an exported declaration introduces the same name with the same classification in the module’s export declaration space. For example, the declaration

  1. export function point(x: number, y: number) {
  2. return { x, y };
  3. }

introduces a local name point and an exported name point that both reference the function.

11.3.4.2 Export Default Declarations

Export default declarations provide short-hand syntax for exporting an entity named default.

  ExportDefaultImplementationElement:   exportdefaultFunctionDeclaration   exportdefaultGeneratorDeclaration   exportdefaultClassDeclaration   exportdefaultAssignmentExpression;

  ExportDefaultDeclarationElement:   exportdefaultAmbientFunctionDeclaration   exportdefaultAmbientClassDeclaration   exportdefaultIdentifierReference;

An ExportDefaultImplementationElement or ExportDefaultDeclarationElement for a function, generator, or class introduces a value named default, and in the case of a class, a type named default, in the containing module’s export declaration space. The declaration may optionally specify a local name for the exported function, generator, or class. For example, the declaration

  1. export default function point(x: number, y: number) {
  2. return { x, y };
  3. }

introduces a local name point and an exported name default that both reference the function. The declaration is effectively equivalent to

  1. function point(x: number, y: number) {
  2. return { x, y };
  3. }
  4. export default point;

which again is equivalent to

  1. function point(x: number, y: number) {
  2. return { x, y };
  3. }
  4. export { point as default };

An ExportDefaultImplementationElement or ExportDefaultDeclarationElement for an expression consisting of a single identifier must name an entity declared in the current module or the global namespace. The declaration introduces an entity named default, with the same classification as the referenced entity, in the containing module’s export declaration space. For example, the declarations

  1. interface Point {
  2. x: number;
  3. y: number;
  4. }
  5. function Point(x: number, y: number): Point {
  6. return { x, y };
  7. }
  8. export default Point;

introduce a local name Point and an exported name default, both with a value and a type meaning.

An ExportDefaultImplementationElement for any expression but a single identifier introduces a value named default in the containing module’s export declaration space. For example, the declaration

  1. export default "hello";

introduces an exported value named default of type string.

11.3.4.3 Export List Declarations

An export list declaration exports one or more entities from the current module or a specified module.

  ExportListDeclaration:   export*FromClause;   exportExportClauseFromClause;   exportExportClause;

An ExportListDeclaration without a FromClause exports entities from the current module. In a declaration of the form

  1. export { x };

the name x must reference an entity declared in the current module or the global namespace, and the declaration introduces an entity with the same name and meaning in the containing module’s export declaration space.

An ExportListDeclaration with a FromClause re-exports entities from a specified module. In a declaration of the form

  1. export { x } from "mod";

the name x must reference an entity in the export member set of the specified module, and the declaration introduces an entity with the same name and meaning in the containing module’s export declaration space. No local bindings are created for x.

The ExportClause of an ExportListDeclaration can specify multiple entities and may optionally specify different names to be used for the exported entities. For example, the declaration

  1. export { x, y as b, z as c };

introduces entities named x, b, and c in the containing module’s export declaration space with the same meaning as the local entities named x, y, and z respectively.

An ExportListDeclaration that specifies * instead of an ExportClause is called an export star declaration. An export star declaration re-exports all members of a specified module.

  1. export * from "mod";

Explicitly exported members take precedence over members re-exported using export star declarations, as described in the following section.

11.3.4.4 Export Member Set

The export member set of a particular module is determined by starting with an empty set of members E and an empty set of processed modules P, and then processing the module as described below to form the full set of exported members in E. Processing a module M consists of these steps:

  • Add M to P.
  • Add to E each member in the export declaration space of M with a name that isn’t already in E.
  • For each export star declaration in M, in order of declaration, process the referenced module if it is not already in P.

A module’s instance type is an object type with a property for each member in the module’s export member set that denotes a value.

If a module contains an export assignment it is an error for the module to also contain export declarations. The two types of exports are mutually exclusive.

11.3.5 Export Assignments

Export assignments exist for backward compatibility with earlier versions of TypeScript. An export assignment designates a module member as the entity to be exported in place of the module itself.

  ExportAssignment:   export=IdentifierReference;

A module containing an export assignment can be imported using an import require declaration (11.3.3), and the local alias introduced by the import require declaration then takes on all meanings of the identifier named in the export assignment.

A module containing an export assignment can also be imported using a regular import declaration (11.3.2) provided the entity referenced in the export assignment is declared as a namespace or as a variable with a type annotation.

Assume the following example resides in the file ‘point.ts’:

  1. export = Point;
  2. class Point {
  3. constructor(public x: number, public y: number) { }
  4. static origin = new Point(0, 0);
  5. }

When ‘point.ts’ is imported in another module, the import alias references the exported class and can be used both as a type and as a constructor function:

  1. import Pt = require("./point");
  2. var p1 = new Pt(10, 20);
  3. var p2 = Pt.origin;

Note that there is no requirement that the import alias use the same name as the exported entity.

11.3.6 CommonJS Modules

The CommonJS Modules definition specifies a methodology for writing JavaScript modules with implied privacy, the ability to import other modules, and the ability to explicitly export members. A CommonJS compliant system provides a ‘require’ function that can be used to synchronously load other modules to obtain their singleton module instance, as well as an ‘exports’ variable to which a module can add properties to define its external API.

The ‘main’ and ‘log’ example from section 11.3 above generates the following JavaScript code when compiled for the CommonJS Modules pattern:

File main.js:

  1. var log_1 = require("./log");
  2. log_1.message("hello");

File log.js:

  1. function message(s) {
  2. console.log(s);
  3. }
  4. exports.message = message;

A module import declaration is represented in the generated JavaScript as a variable initialized by a call to the ‘require’ function provided by the module system host. A variable declaration and ‘require’ call is emitted for a particular imported module only if the imported module, or a local alias (section 10.3) that references the imported module, is referenced as a PrimaryExpression somewhere in the body of the importing module. If an imported module is referenced only as a NamespaceName or TypeQueryExpression, nothing is emitted.

An example:

File geometry.ts:

  1. export interface Point { x: number; y: number };
  2. export function point(x: number, y: number): Point {
  3. return { x, y };
  4. }

File game.ts:

  1. import * as g from "./geometry";
  2. let p = g.point(10, 20);

The ‘game’ module references the imported ‘geometry’ module in an expression (through its alias ‘g’) and a ‘require’ call is therefore included in the emitted JavaScript:

  1. var g = require("./geometry");
  2. var p = g.point(10, 20);

Had the ‘game’ module instead been written to only reference ‘geometry’ in a type position

  1. import * as g from "./geometry";
  2. let p: g.Point = { x: 10, y: 20 };

the emitted JavaScript would have no dependency on the ‘geometry’ module and would simply be

  1. var p = { x: 10, y: 20 };

11.3.7 AMD Modules

The Asynchronous Module Definition (AMD) specification extends the CommonJS Modules specification with a pattern for authoring asynchronously loadable modules with associated dependencies. Using the AMD pattern, modules are emitted as calls to a global ‘define’ function taking an array of dependencies, specified as module names, and a callback function containing the module body. The global ‘define’ function is provided by including an AMD compliant loader in the application. The loader arranges to asynchronously load the module’s dependencies and, upon completion, calls the callback function passing resolved module instances as arguments in the order they were listed in the dependency array.

The “main” and “log” example from above generates the following JavaScript code when compiled for the AMD pattern.

File main.js:

  1. define(["require", "exports", "./log"], function(require, exports, log_1) {
  2. log_1.message("hello");
  3. }

File log.js:

  1. define(["require", "exports"], function(require, exports) {
  2. function message(s) {
  3. console.log(s);
  4. }
  5. exports.message = message;
  6. }

The special ‘require’ and ‘exports’ dependencies are always present. Additional entries are added to the dependencies array and the parameter list as required to represent imported modules. Similar to the code generation for CommonJS Modules, a dependency entry is generated for a particular imported module only if the imported module is referenced as a PrimaryExpression somewhere in the body of the importing module. If an imported module is referenced only as a NamespaceName, no dependency is generated for that module.