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
- The basics of TypeScript
- Common TypeScript Types (Part 1)
- Common TypeScript types (part 2)
- TypeScript type narrowing
- The function of TypeScript
- TypeScript object type
- The generic of TypeScript
- TypeScript’s Keyof operator
- TypeScript’s Typeof operator
- TypeScript index access types
- TypeScript conditional types
- TypeScript mapping types
- TypeScript template literal types
- TypeScript (I)
- 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.