The official TypeScript documentation has been updated for a long time, but the Chinese documentation I could find was still in older versions. Therefore, some new and revised chapters are translated and sorted out.

This translation is from the TypeScript Handbook “Module” chapter.

This paper does not strictly follow the translation of the original text, and some of the content has also been explained and supplemented.

Module

JavaScript has a long history of handling modular code, and TypeScript has been following suit since 2012 and now implements support for many formats. Over time, however, the community and JavaScript specifications have converged into a format known as the ES module (or ES6 module), which is also known as the import/export syntax.

The ES module was added to the JavaScript specification in 2015, and by 2020, most Web browsers and JavaScript runtime environments are widely supported.

This chapter will cover the ES module and its popular predecessor CommonJS module.exports = syntax. You can find other module modes in the Modules section.

How JavaScript Modules are Defined

In TypeScript, as in ECMAScript 2015, any file that contains a top-level import or export is considered a module.

Conversely, a file without top-level import and export declarations is considered a script, and its contents are available globally.

A module executes in its own scope, not in the global scope. This means that variables, functions, classes, etc. declared in a module are not visible to code outside the module unless you export them.

Conversely, to consume a value, function, class, interface, etc. exported from another module, it also needs to be imported in the imported format first.

Non-modules

Before we begin, we need to understand what TypeScript considers a module. The JavaScript specification states that any JavaScript file that does not have an export or top-level await should be considered a script, not a module.

In a script file, variables and types are declared in the shared global scope, and it is assumed that you either use the outFile compilation option to combine multiple input files into one output file, or load these files in HTML using multiple

If you have a file that does not currently have any import or export, but you want it to be treated as a module, add this line:

export {};
Copy the code

This changes the file to a module that does not export anything, and this syntax works regardless of your module’s target.

Modules in TypeScript

There are three main things to consider when writing module-based code in TypeScript:

  • Syntax: What syntax should I use if I want to export or import?
  • Module resolution: What is the relationship between module names (or paths) and disk files?
  • Module export goal: What does the exported JavaScript module look like?

ES Module Syntax

A file can be declared as a major export using export default:

// @filename: hello.ts
export default function helloWorld() {
  console.log("Hello, world!");
}
Copy the code

Then import it this way:

import hello from "./hello.js";
hello();
Copy the code

In addition to the default export, you can export more than one variable and function by omitting the export syntax of default:

// @filename: maths.ts
export var pi = 3.14;
export let squareTwo = 1.41;
export const phi = 1.61;
 
export class RandomNumberGenerator {}
 
export function absolute(num: number) {
  if (num < 0) return num * -1;
  return num;
}
Copy the code

These can be introduced in other files using the import syntax:

import { pi, phi, absolute } from "./maths.js";
 
console.log(pi);
const absPhi = absolute(phi);
// const absPhi: number
Copy the code

Additional Import Syntax

An import can also be renamed using a format similar to import {old as new} :

import { pi asPI}from "./maths.js";
 
console.log (PI);// (alias) var π: number
/ / import PI
Copy the code

You can mix up the syntax above and write it as a single import:

// @filename: maths.ts
export const pi = 3.14;
export default class RandomNumberGenerator {}
 
// @filename: app.ts
import RNGen, { pi asPI}from "./maths.js";
 
RNGen;
 
(alias) class RNGen
import RNGen
 
console.log(PI);
// (alias) const π: 3.14
/ / import PI
Copy the code

You can take all the exported objects and put them in a separate namespace using * as name:

// @filename: app.ts
import * as math from "./maths.js";
 
console.log(math.pi);
const positivePhi = math.absolute(math.phi);

// const positivePhi: number
Copy the code

You can import a file by importing “./file”, which does not reference any variables to your current module:

// @filename: app.ts
import "./maths.js";
 
console.log("3.14");
Copy the code

In this example, import does nothing; however, all the math.ts code executes, triggering side-effects that affect other objects.

TypeScript Specific ES Module Syntax

Types can be exported and imported using the same syntax as JavaScript values:

// @filename: animal.ts
export type Cat = { breed: string; yearOfBirth: number };
 
export interface Dog {
  breeds: string[];
  yearOfBirth: number;
}
 
// @filename: app.ts
import { Cat, Dog } from "./animal.js";
type Animals = Cat | Dog;
Copy the code

TypeScript already extends the import syntax to facilitate type imports in two ways:

Import type

// @filename: animal.ts
export type Cat = { breed: string; yearOfBirth: number };
// 'createCatName' cannot be used as a value because it was imported using 'import type'.
export type Dog = { breeds: string[]; yearOfBirth: number };
export const createCatName = () = > "fluffy";
 
// @filename: valid.ts
import type { Cat, Dog } from "./animal.js";
export type Animals = Cat | Dog;
 
// @filename: app.ts
import type { createCatName } from "./animal.js";
const name = createCatName();
Copy the code

Inline Type imports

TypeScript 4.5 also allows separate imports. You need to use the type prefix to indicate that a type is being imported:

// @filename: app.ts
import { createCatName, type Cat, type Dog } from "./animal.js";
 
export type Animals = Cat | Dog;
const name = createCatName();
Copy the code

These can run a non-typescript compiler such as Babel, SWC, or ESBuild knowing what imports can be safely removed.

The difference between imported types and built-in type imports is that one is an import syntax and the other is a type-only import

ES Module Syntax with CommonJS Behavior

TypeScript’s ES module syntax is directly related to CommonJS and AMD Required. Imports using the ES module, like require, are suitable for most situations. But this syntax ensures that you have a 1-to-1 match in TypeScript files with CommonJS output:

import fs = require("fs");
const code = fs.readFileSync("hello.ts"."utf8");
Copy the code

You can learn more about this syntax on the module reference page.

CommonJS Syntax

CommonJS is the format for most modules in NPM. Even if you’re writing ES module syntax, knowing how CommonJS syntax works will make debugging easier.

Exporting

Export identifiers by setting the global Module exports property.

function absolute(num: number) {
  if (num < 0) return num * -1;
  return num;
}
 
module.exports = {
  pi: 3.14.squareTwo: 1.41.phi: 1.61,
  absolute,
};
Copy the code

These files can be imported via a require statement:

const maths = require("maths");
maths.pi;
// any
Copy the code

You can use JavaScript’s deconstruction syntax to simplify the code a bit:

const { squareTwo } = require("maths");
squareTwo;
// const squareTwo: any
Copy the code

CommonJS and ES Modules interop

Because of the difference between default exports and module declaration space object exports, CommonJS and ES modules are not very suitable for use together. TypeScript has an esModuleInterop compilation option to reduce conflicts between the two constraints.

TypeScript’s Module Resolution Options

Module parsing is the process of taking a string from an import or require statement and deciding which file the character points to.

TypeScript includes two parsing policies: Classic and Node. Classic, which is the default option when the compile option Module is not commonJS, includes backward compatibility. The Node policy replicates how Nodejs works in CommonJS mode, with extra checks on.ts and.d.ts.

There are a number of TSConfig flags that affect TypeScript module policies: moduleResolution, baseUrl, Paths, rootDirs.

For full details on how these strategies work, you can refer to Module Resolution.

TypeScript’s Module Output Options

There are two options to affect JavaScript output files:

  • Target determines which JS features are degraded (converted to be usable in older JavaScript runtime environments) and which remain intact.
  • The module determines which code in the module will be used to interact with each other

Which target you use depends on the environment in which you expect your code to run. These can be: the oldest browser you need to support, the oldest version of Nodejs you expect your code to run, or some unique runtime environment constraint such as Electron.

All communication between modules is through the module loader, and compiling the module option determines which one is used. At run time, the module loader locates and executes all of a module’s dependencies before executing it.

For example, here’s a TypeScript file that uses ES Module syntax to show the result of different Module options:

import { valueOfPi } from "./constants.js";
 
export const twoPi = valueOfPi * 2;
Copy the code

ES2020

import { valueOfPi } from "./constants.js";
export const twoPi = valueOfPi * 2;
Copy the code

CommonJS

"use strict";
Object.defineProperty(exports."__esModule", { value: true });
exports.twoPi = void 0;
const constants_js_1 = require("./constants.js");
exports.twoPi = constants_js_1.valueOfPi * 2;
Copy the code

UMD

(function (factory) {
    if (typeof module= = ="object" && typeof module.exports === "object") {
        var v = factory(require.exports);
        if(v ! = =undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require"."exports"."./constants.js"], factory);
    }
})(function (require.exports) {
    "use strict";
    Object.defineProperty(exports."__esModule", { value: true });
    exports.twoPi = void 0;
    const constants_js_1 = require("./constants.js");
    exports.twoPi = constants_js_1.valueOfPi * 2;
});
 
Copy the code

Note that ES2020 is already the same as the original index.ts file.

You can see all the available options and what their corresponding compiled JavaScript code looks like on the TSConfig module page.

TypeScript namespaces

TypeScript has its own module format called Namespaces. It predates the ES module standard. This syntax has a set of features that can be used to create complex definition files and can still be seen in DefinitelyTyped. When not deprecated, the main features of namespaces are still in the ES module, and we recommend that you use them aligned with JavaScript orientation. You can learn more on the namespace page.

The TypeScript series

  1. The basics of TypeScript
  2. Common TypeScript Types (Part 1)
  3. Common TypeScript types (part 2)
  4. TypeScript type narrowing
  5. The function of TypeScript
  6. TypeScript object type
  7. The generic of TypeScript
  8. TypeScript’s Keyof operator
  9. TypeScript’s Typeof operator
  10. TypeScript index access types
  11. TypeScript conditional types
  12. TypeScript mapping types
  13. TypeScript template literal types
  14. TypeScript (I)
  15. TypeScript (part 2)

Wechat: “MQyqingfeng”, add me Into Hu Yu’s only readership group.

If there is any mistake or not precise place, please be sure to give correction, thank you very much. If you like or are inspired by it, welcome star and encourage the author.