• Description : There is currently no Chinese translation of the latest official documents of TypeScript on the Internet, so there is such a translation plan. Because I am also a beginner in TypeScript, I cannot guarantee that the translation will be 100% accurate. If there are errors, please point out in the comment section;
  • translation content : The tentative translation content is TypeScript Handbook , and other parts of the translation document will be added later;
  • project address : TypeScript-Doc-Zh , if it helps you, you can click a star~

The official document address of this chapter: Modules

Module

There are many ways to deal with modular code, and JavaScript has a long history in this area. TypeScript was born in 2012 and supports many modular solutions. But over time, the community and the JavaScript specification reached a consensus on a solution called ES modules (or ES6 modules). You may have heard of its import/export syntax.

The ES module was included in the JavaScript specification in 2015, and by 2020, it has been supported by most web browsers and JavaScript runtimes.

This manual will focus on the ES module and module.exports = syntax. module in the "Reference" chapter, you can learn more about other modular solutions.

How are JavaScript modules defined

Like ECMAScript 2015, TypeScript will treat any import or export as a module.

Conversely, a file that does not contain the top-level import or export declarations will be treated as a script, and its content can be accessed in the global scope (and therefore visible to the module).

The module executes in its own scope, not in the global scope. This means that variables, functions, and classes declared in a module are not visible outside the module unless they are explicitly exported using one of the export methods. Conversely, in order to use variables, functions, classes, etc., exported from a different module, you also need to use one of the import methods to import them.

Non-module

Before we start, there is a very important thing to figure out, and that is what TypeScript treats as a module. The JavaScript specification states that any export or the top-level await should be treated as a script, not a module.

Variables and types declared in a script file will be in the shared global scope, and usually, you will use the outFile compilation option to merge multiple input files into one output file, or use multiple in the HTML file A <script> tag goes (in the correct order!) to load the file.

If your file does not currently have any import or export , but you want to treat it as a module, you can add the following line of code:

export {};

This will convert the file into a module that does not export anything. No matter what your module goal is, this syntax can take effect.

Modules in TypeScript

When writing module-based code in TypeScript, there are three main things to consider:

  • Syntax: What syntax do I want to use to import and export?
  • module analysis: module name (or path) and the file on the disk?
  • module output target: What should the JavaScript module generated by

ES module syntax

A file can declare a main export export default

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

Then import through the following statement:

import hello from "./hello.js";
hello();

In addition to the default export, you can also omit default and directly use export export multiple variables and functions:

// @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;
}

You can import it in another file import

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

Other import syntax

You can rename an import import {old as new}

import { pi as π } from "./maths.js";
 
console.log(π);
            ^
     //(alias) var π: number
    // import π

You can mix the above syntax into a single import :

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

With * as name , you can accept all exported objects and put them in a single namespace:

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

Using import "./file" , you can import only the file in the current module without importing any variables in the file:

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

In this case, import will not do anything. However, math.ts will be executed, which may trigger side effects and affect other objects.

TypeScript-specific ES module syntax

You can use the same syntax as JavaScript values to export and import types:

// @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;

TypeScript import grammar of 061bb2344c641a for two purposes, allowing it to declare type imports:

import type

The import statement can only import types:

// @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();

inline type import

TypeScript 4.5 also allows a single import to use the type prefix to indicate that the imported reference is of a type:

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

All of this allows non-TypeScript translation tools such as Babel, swc, or esbuild to know which imports can be safely removed.

ES module syntax with CommonJS behavior

The ES module syntax of TypeScript can be directly related to require In most cases, importing using ES modules is the same as using require in the same environment, but this syntax can ensure that there is a one-to-one match between your TypeScript file and CommonJS output:

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

You can learn more about this syntax in the reference section module

CommonJS syntax

CommonJS is the modular scheme adopted by most npm packages. Even if you use ES module syntax when writing your code, a brief understanding of how CommonJS syntax works can help simplify your debugging process.

Export

By setting the exports attribute to a module , you can export the identifier:

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

Afterwards, these files require

const maths = require("maths");
maths.pi;
      ^
   // any

Or you can use JavaScript's deconstruction syntax to import only part of the content:

const { squareTwo } = require("maths");
squareTwo;
    ^
  // const squareTwo: any

Interoperability of CommonJS and ES modules

Due to the difference between default import and module namespace object import, there is a functional mismatch between CommonJS and ES modules. TypeScript provides a compilation option esModuleInterop to reduce the conflict between these two different sets of constraints.

TypeScript module resolution options

Module parsing is a process that refers import or require statement and determining the file indicated by the string.

TypeScript uses two parsing strategies: Classic and Node. The Classic strategy is used to achieve backward compatibility. When the compilation option module is not commonjs , this strategy is used by default. The Node strategy replicates the way Node.js works in CommonJS mode, and provides additional checks .ts and .d.ts

There are many TSConfig options that affect the module strategy in TypeScript, including: moduleResolution , baseUrl , paths , rootDirs .

For more information on how these strategies work, please read module resolution .

TypeScript module output options

There are two options that affect the final output of JavaScript:

  • target will determine which JS features will be downgraded (converted and run in an older JavaScript runtime) and which JS features will be retained
  • module will determine the code used for interaction between modules

Which target use depends on the features available at the JavaScript runtime that you want to execute TypeScript code. Such a runtime can be: the oldest browser you support, the lowest version of Node.js you want to run, or the runtime-such as the unique constraints of Electron.

All communication between modules is carried out through a module loader, and the compilation option module will determine which one should be used. At runtime, the module loader is responsible for locating and executing all dependencies of the module before executing the module.

For example, this is a TypeScript file that uses ES module syntax:

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

The following is the compilation result after module

ES2020
import { valueOfPi } from "./constants.js";
export const twoPi = valueOfPi * 2;
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;
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;
});
Note: ES2020 is actually the same as the original index.ts.

You can learn about all the available options and the corresponding output JavaScript code in the "module" section of the reference chapter TSConfig

TypeScript namespace

TypeScript has its own module format called "namespace", which appeared earlier than the ES module standard. This syntax provides many useful features to create complex definition files, and is still widely used in DefinitelyTyped . Although this syntax has not been deprecated, since ES modules already have most of the features of namespaces, we recommend that you use ES modules to be consistent with JavaScript. You can learn more about this in the reference section of the namespace


Chor
2k 声望5.9k 粉丝