Welcome to wechat Public account: Front Reading Room

A quick note on terminology: It’s important to note that terms have changed in TypeScript 1.5. “Internal modules” are now called “namespaces.” “External modules” are now referred to simply as “modules” to be consistent with ECMAScript 2015 terminology (that is, module X {is equivalent to the now recommended namespace X {).

introduce

This article describes how to use namespaces (formerly called “internal modules”) in TypeScript to organize your code. As we mentioned in the terminology note, “internal modules” are now called “namespaces.” In addition, any place where the Module keyword is used to declare an internal module should be replaced with the namespace keyword. This avoids confusing new users with similar names.

The first step

Let’s write a program first and use this example throughout the article. Let’s define a few simple string validators, assuming you’ll use them to validate user input in a form or to validate external data.

All the validators are in one file

interface StringValidator {
    isAcceptable(s: string) :boolean;
}

let lettersRegexp = /^[A-Za-z]+$/;
let numberRegexp = / ^ [0-9] + $/;

class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
        returnlettersRegexp.test(s); }}class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5&& numberRegexp.test(s); }}// Some samples to try
let strings = ["Hello"."98052"."101"];

// Validators to use
let validators: { [s: string]: StringValidator; } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();

// Show whether each string passed each validator
for (let s of strings) {
    for (let name in validators) {
        let isMatch = validators[name].isAcceptable(s);
        console.log(` '${ s }' ${ isMatch ? "matches" : "does not match" } '${ name }'. `); }}Copy the code

The namespace

As more validators are added, we need a way to organize our code so that we can record their types without worrying about naming conflicts with other objects. Therefore, we wrap validators in a namespace instead of putting them under the global namespace.

In the following example, all the validator-related types are placed in a namespace called Validation. Because we want these interfaces and classes to be accessible outside of the namespace, we need to use export. In contrast, the variables lettersRegexp and numberRegexp are implementation details and do not need to be exported, so they are not accessible outside the namespace. The test code at the end of the file, because it is outside the namespace to visit, so you need to limit the name of the type, such as Validation. LettersOnlyValidator.

Validators that use the namespace

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string) :boolean;
    }

    const lettersRegexp = /^[A-Za-z]+$/;
    const numberRegexp = / ^ [0-9] + $/;

    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            returnlettersRegexp.test(s); }}export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5&& numberRegexp.test(s); }}}// Some samples to try
let strings = ["Hello"."98052"."101"];

// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

// Show whether each string passed each validator
for (let s of strings) {
    for (let name in validators) {
        console.log(`"${ s }"-${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`); }}Copy the code

Separate to multiple files

As the application gets larger, we need to separate the code into different files for easier maintenance.

Namespaces in multiple files

Now, let’s split the Validation namespace into multiple files. Although they are different files, they are in the same namespace and are used as if they were defined in one file. Because of the dependencies between the different files, we added reference tags to tell the compiler about the association between the files. Our test code remains the same.

Validation.ts

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string) :boolean; }}Copy the code

LettersOnlyValidator.ts

/// <reference path="Validation.ts" />
namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            returnlettersRegexp.test(s); }}}Copy the code

ZipCodeValidator.ts

/// <reference path="Validation.ts" />
namespace Validation {
    const numberRegexp = / ^ [0-9] + $/;
    export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
            return s.length === 5&& numberRegexp.test(s); }}}Copy the code

Test.ts

/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />

// Some samples to try
let strings = ["Hello"."98052"."101"];

// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

// Show whether each string passed each validator
for (let s of strings) {
    for (let name in validators) {
        console.log(`"${ s }"-${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`); }}Copy the code

When it comes to multiple files, we have to make sure that all compiled code is loaded. There are two ways we can do this.

The first way to compile all the input files into one output file is to use the –outFile tag:

tsc --outFile sample.js Test.ts
Copy the code

The compiler automatically sorts the output based on the reference tags in the source code. You can also specify each file individually.

tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
Copy the code

In the second way, we can compile each file (the default) so that each source file generates a JavaScript file. Then, all the generated JavaScript files are imported in the correct order on the page using the

MyTestPage.html (excerpt)

    <script src="Validation.js" type="text/javascript" />
    <script src="LettersOnlyValidator.js" type="text/javascript" />
    <script src="ZipCodeValidator.js" type="text/javascript" />
    <script src="Test.js" type="text/javascript" />
Copy the code

The alias

Another way to simplify namespace operations is to use import q = X.Y.Z to give short names to commonly used objects. Not to be confused with the import x = require(‘name’) syntax used to load modules, the syntax here is to create an alias for the specified symbol. You can use this method to create aliases for any identifier, including objects in imported modules.

namespace Shapes {
    export namespace Polygons {
        export class Triangle {}export class Square {}}}import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // Same as "new Shapes.Polygons.Square()"
Copy the code

Note that instead of using the require keyword, we assign directly using the qualified name of the imported symbol. This is similar to using var, but it also applies to types and imported symbols that have namespace meaning. Importantly, for values, import generates a different reference than the original symbol, so changing the var value of the alias does not affect the value of the original variable.

Use other JavaScript libraries

To describe the types of libraries that are not written in TypeScript, we need to declare the API that the library exports. Since most libraries provide only a few top-level objects, namespaces are a good way to represent them.

We call it a declaration because it is not a concrete implementation of an external program. We usually write these statements in.D.ts. If you are familiar with C/C++, you can think of them as.h files. Let’s look at some examples.

External namespace

The popular library D3 defines its functionality in global object D3. Because the library is loaded via a

D3.d.ts (some excerpts)

declare namespace D3 {
    export interface Selectors {
        select: {
            (selector: string): Selection;
            (element: EventTarget): Selection;
        };
    }

    export interface Event {
        x: number;
        y: number;
    }

    export interface Base extends Selectors {
        event: Event;
    }
}

declare var d3: D3.Base;
Copy the code

Welcome to wechat Public account: Front Reading Room