Module concept in typescript

Time:2021-7-28

Modules

JavaScript has a long history of dealing with modular code. Typescript has implemented support for many of these formats since its launch in 2012, but over time, the community and JavaScript specifications have been integrated into a format called es module (or ES6 module). You may know that it is import / export syntax.

Es modules was added to the JavaScript specification in 2015 and will be widely supported in most web browsers and JavaScript runtime by 2020.

For emphasis, the manual will cover the ES module and its popular precursor commonjs module. Exports = syntax. You can find information about other module patterns in the References section under the module.

How JavaScript Modules are Defined

In typescript, as in ECMAScript 2015, any file containing a top-level import or export is treated as a module.

Conversely, files without any top-level import or export declarations are treated as scripts, and their contents are available globally (and therefore for modules).

Modules execute in their own scope, not in the global scope. Unless they are explicitly declared in the exported module, they are not visible in the exported module, etc. On the contrary, to use variables, functions, classes, interfaces, etc. exported from different modules, you must use one of the import forms.

Non module

Before we get started, it’s important to understand what typescript sees as modules. The JavaScript specification states that any JavaScript file that is not exported or top-level await should be treated as a script rather than a module.

In the script file, variables and types are declared to be in the shared global scope, and it is assumed that you will use the — outfile compiler option to connect multiple input files to an output file, or use multiple output files in it
\< script > tag your HTML to load these files (in the correct order!).

If you have a file that does not currently have any imports or exports, but you want to be treated as a module, add the following line:

export {};

This changes the file to a module that does not export anything. This syntax is valid regardless of your module goals.

Es module syntax

The file can declare the main export through export default:

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

This is then imported via:


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

In addition to the default export, you can export multiple variables and functions by omitting the default export:

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

Import in braces in another file:

import { pi, phi, absolute } from "./maths.js";

console.log(pi);
const absPhi = absolute(phi);

You can also use the import alias syntax:


import { pi as π } from "./maths.js";

console.log(π);

You can put all exported objects into a namespace with * as the name:

// @filename: app.ts
import * as math from "./maths.js";

console.log(math.pi);
const positivePhi = math.absolute(math.phi);

What are the consequences of importing a file directly?

// @filename: app.ts
import "./maths.js";

console.log("3.14");

In this case, the import does nothing. However, all the code in math.ts has been evaluated, which may trigger side effects that affect other objects.

Typescript enhances the import syntax. You can only import the type definitions in the module.

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

TypeScript’s Module Resolution Options

Module parsing is the process of obtaining a string from an import or require statement and determining which file the string refers to.

Typescript includes two parsing strategies: classic and node. Classic, the default value when the compiler flag module is not commonjs, which is included for backward compatibility. The node policy copies how node.js works in commonjs mode and additionally checks. Ts and. D.ts.

There are many tsconfig flags that affect module policies in typescript: moduleresolution, baseurl, paths, rootdirs.

For complete details on how these policies work, you can consult the module solutions.

TypeScript’s Module Output Options

There are two options that affect the JavaScript output emitted:

  • Target determines which JS features are degraded (converted to run in older JavaScript runtimes) and which remain unchanged
  • Module: a module that determines which code is used to interact between modules

The target you use depends on the functionality available in the JavaScript runtime in which you want to run typescript code. This may be: the oldest web browser you support, the lowest version of node.js you want to run or may come from, comes from unique constraints of your runtime — such as electron.

All communication between modules is carried out through the module loader, and the compiler flag module determines which one to use. At runtime, the module loader is responsible for locating and executing all dependencies of the module before execution.

For example, this is a typescript file using ES module syntax, showing several different options for the module.

The following is the original typescript file:

import { valueOfPi } from "./constants.js";

export const twoPi = valueOfPi * 2;

The following is es2020 output:

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

Here is 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;

Here is 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;
});