Modules Back
Note: since
TypeScript@1.5, "internal modules" have been called "namespaces", while "external modules" have been called "modules".
As introduced within ECMAScript 2015, JavaScript has a concept named "modules", which has been shared within TypeScript. Modules has its own scope, rather than the global one, which means that variables, functions, classes, etc. declared in a module are not visible outside the module. To create or use a module, we may need to use export and import statements.
In more detailed, it is the same with JavaScript that a file without any top-level import or export declarations is treated as a file within a global scope. What the difference is that we can also import or export interfaces, types or any other new kinds of declarations in TypeScript.
/** validator.ts */
export interface validator {
isAcceptable(s: string): boolean;
}import {validator} from './validator.ts';
const stringValidator: validator = {
isAcceptable(s: string): boolean {
return !!s.length;
},
}As for detailed syntax around modules, it is not going to discuss in this chapter, except some special points in TypeScript.
To compile a TypeScript file within CommonJS style, we need to use --module commonjs, while --module amd for AMD style:
tsc --module commonjs test.tsWhen it comes to dynamic module loading, typeof keywords help us to keep type safety when loading modules within conditions:
-
CommonJS Style:
declare function require(module: string): any; import { Zip } from './zipModule'; if (need) { const Zip: typeof Zip = require('./zipModule'); /** ... */ }
-
AMD Style:
declare function defined(module: string[], callback: (...args: any[]) => void): void; import { Zip } from './zipModule'; if (need) { defined(['./zipModule'], (Zip: typeof Zip) => { /** ... */ }); }
In computer science, it is common to say that declarations that do not define an implementation "ambient", and typically such modules are defined in *.d.ts files, which you can think of as head files within C/C++.
It is more convenient to write declarations into one large head file, rather than define each one in each module. Take the following snippet as an example. Assume that we have a head file, named as node.d.ts, in compose of two declarations:
/** node.d.ts */
declare module 'url' {
export interface Url {
protocol?: string;
host?: string;
path?: string;
hash?: string;
}
export function parse(url: string): Url;
}
declare module 'path' {
export function normalize(path: string): string;
export function join(...paths: any[]): string;
}And then we can reference it and load modules using import statements:
/// <reference path="node.d.ts">
import * as URL from 'url';
const url: URL.Url = URL.parse('http://aleen42.github.io/PersonalWiki');If we do not want to take the time to write out declarations before using a new module, we can use s shorthand declaration instead:
declare module 'url';When familiar with Webpack, we usually use different loaders to help us load modules from different types of files like *.css , raw text, etc. For such declarations, we can use a prefix to indicate the special loading statements:
/** modules.d.ts */
declare module 'text!*' {
const content: string;
export default content;
}And then reference it:
/// <reference path="modules.d.ts"
import log from 'text!./log.txt';Export things in a nested structure has reduced its code readability, especially when exporting static methods, it is advised exporting directly in the top level rather than inside the class.
export default class Util {
/** bad */
static test() { console.log('test'); }
}Why not exporting directly?
export class Util {}
export function test() { console.log('test'); }Simply, when exporting single class or function inside a module, use export default statements help us to reduce bytes during importing:
/** help.ts */
export default function help() { /** ... */ }/** rather than import {help} from './help' */
import help from './help';In addition, it is more convenient to name whatever we want during importing:
import h from './help';When exporting multiple objects, put them all at top-level like the following snippet:
/** help.ts */
export class Type { /** ... */ }
export function help() { /** ... */ }And then explicitly import them, except importing a large number of things:
import {Type, help} from './help';Use the namespace import pattern while importing a large number of things:
/** modules.ts */
export class Dog { /** ... */ }
export class Cat { /** ... */ }
export class Tree { /** ... */ }
export class Flower { /** ... */ }import * as Modules from './modules';modules do not merge like global namespace objects would, and if we need to modify some modules, it is recommended to not mutate them, but rather export a new module with mutations. It is quite similar to object-oriented programming (OOP).
import Base from './base';
export default class Mutate extends Base {
/** ... */
}