1
头图

The official documentation of TypeScript has long been updated, but the Chinese documents I can find are still in the older version. Therefore, some new and revised chapters have been translated and sorted out.

The translation of this article is compiled from the chapter Module

This article does not strictly follow the original translation, but also explains and supplements part of the content.

Module

JavaScript has a long history of dealing with modular code. TypeScript has been following up since 2012 and has now implemented support for many formats. However, over time, the community and JavaScript specifications have converged to a format called ES modules (or ES6 modules), which is what we know as the import/export grammar.

The ES module was added to the JavaScript specification in 2015. By 2020, most web browsers and JavaScript runtime environments have been widely supported.

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

How JavaScript Modules are Defined (How JavaScript Modules are Defined)

In TypeScript, just like in ECMAScript 2015, any import or export is considered a module.​

Correspondingly, a file without top-level import and export declarations will be considered a script, and its content will be available in the global scope.

The module will execute 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 explicitly export these values.

Correspondingly, if you want to consume a value, function, class, interface, etc. exported from another module, you also need to use the imported format to be imported first.

Non-modules

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

In a script file, variables and types will be declared in a shared global scope, it will be assumed that you either use outFile compilation option to merge multiple input files into one output file, or use multiple <script> The label loads these files.

If you have a file and there is no import or export , but you want it to be processed as a module, add this line of code:

export {};

This will change the file to a module that does not export any content. This syntax can take effect, no matter what your module target is.

Modules in TypeScript

In TypeScript, when writing a module-based code, there are three main things to consider:

  • Syntax: What syntax should I use to export or import?
  • Module analysis: What is the relationship between the module name (or path) and the hard disk file?
  • Module export target: What does the exported JavaScript module look like?

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 in this way:

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

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

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

These can be introduced in other files through the import grammar:

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

Additional Import Syntax

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

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

You can mix the above syntax 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 as π } from "./maths.js";
 
RNGen;
 
(alias) class RNGen
import RNGen
 
console.log(π);
// (alias) const π: 3.14
// import π

You can accept all exported objects and use * as name to put them in a separate namespace:

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

// const positivePhi: number

You can import "./file" , which will not reference any variables to your current module:

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

In this example, import does nothing. However, math.ts will be executed, triggering some side-effects that affect other objects.

TypeScript Specific ES Module Syntax (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;

TypeScript has expanded the import syntax in two aspects to facilitate type import:

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

Inline type imports

TypeScript 4.5 also allows separate imports, you need to use the type prefix to indicate that the imported is a type:

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

These allow a non-TypeScript compiler such as Babel, swc or esbuild to know what imports can be safely removed.

The difference between imported type and built-in type import is that one is the import syntax, and the other is only the import type.

ES Module Syntax with CommonJS Behavior (ES Module Syntax with CommonJS Behavior)

The reason why TypeScript has ES module syntax has a lot to do required Importing using ES module syntax require , but this syntax can ensure that you have a 1-to-1 match in the TypeScript file output by CommonJS:

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

You can learn more about this syntax module reference page

CommonJS Syntax (CommonJS Syntax)

CommonJS is the format of most modules of npm. Even if you are writing ES module syntax, understanding how CommonJS syntax works will help you debug easier.

Exporting

The identifier is derived by setting the exports module

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

These files can be require statement:

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

You can simplify the code a bit using JavaScript's deconstruction syntax:

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

CommonJS and ES Modules interop (CommonJS and ES Modules interop)

Because of the difference between default export and module declaration space object export, CommonJS and ES modules are not very suitable for use together. TypeScript has a esModuleInterop compilation option to reduce the conflict between the two specifications.

TypeScript's Module Resolution Options

Module parsing is the import of extracting character strings from 061d45c063ae3a or require statements, and then determining which file the characters point to.

TypeScript includes two parsing strategies: Classic and Node. Classic, when the compiler option module is not commonjs , it includes backward compatibility. The Node strategy replicates the operation of Nodejs in CommonJS mode, and will have additional checks .ts and .d.ts

There are many TSConfig flags that can affect TypeScript's module strategy: moduleResolution , baseUrl , paths , rootDirs rootDirs 161d45c063ae9.

For the complete details of the work of these strategies, you can refer to Module Resolution .

TypeScript's Module Output Options

There are two options to affect the files output by JavaScript:

  • target determines which JS features will be downgraded (converted to be usable in an older JavaScript runtime environment), and which will remain intact.
  • module determines the module specification adopted by the converted code

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

The compiler option module determines which standard is used for communication between modules. At runtime, the module loader will find and execute all dependencies of this module before executing the module.

For example, this is a TypeScript file using ES Module syntax, showing the module caused by different options:

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

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 that ES2020 is already the same as the original index.ts file.

You can see all the available options and their corresponding compiled JavaScript code TSConfig module

TypeScript namespaces

TypeScript has its own module format called namespaces . It appeared before the ES module standard. This grammar has a series of features that can be used to create complex definition files, which can still be seen in DefinitelyTyped . When it is not obsolete, the main features of the namespace still exist in the ES module. We recommend that you use it in the direction of JavaScript. You can learn more namespace page

TypeScript series

The TypeScript series of articles consists of three parts: official document translation, important and difficult analysis, and practical skills. It covers entry, advanced, and actual combat. It aims to provide you with a systematic learning TS tutorial. The entire series is expected to be about 40 articles. Click here to browse the full series of articles, and suggest to bookmark the site by the way.

WeChat: "mqyqingfeng", add me to the only reader group in Kongyu.

If there are mistakes or not rigorous, please correct me, thank you very much. If you like or have some inspiration, star is welcome, which is also an encouragement to the author.


冴羽
9.3k 声望6.3k 粉丝

17 年开始写前端文章,至今 6 个系列,上百篇文章,全网千万阅读