Модули
У JavaScript долгая история различных способов модулизации кода. TypeScript, появившийся в 2012 году, реализовал поддержку многих из этих форматов, но со временем сообщество и спецификация JavaScript сошлись на формате под названием ES Modules (или ES6 модули). Возможно, вы знаете его как синтаксис import/export.
ES Modules были добавлены в спецификацию JavaScript в 2015 году, а к 2020 году получили широкую поддержку в большинстве веб-браузеров и сред выполнения JavaScript.
Для удобства справочник охватывает как ES Modules, так и его популярного предшественника CommonJS module.exports = синтаксис, и вы можете найти информацию о других шаблонах модулей в разделе «Модули» в справочном разделе по ссылке Модули.
Как определяются JavaScript модули
В TypeScript, так же как и в ECMAScript 2015, любой файл, содержащий верхнеуровневое объявление import или export, считается модулем.
Напротив, файл без верхнеуровневых объявлений import или export обрабатывается как скрипт, содержимое которого доступно в глобальной области видимости (и, следовательно, для модулей тоже).
Модули выполняются в своей собственной области видимости, а не в глобальной. Это означает, что переменные, функции, классы и т. д., объявленные в модуле, не видны вне модуля, если они не явно экспортированы с помощью одного из форм экспорта. Напротив, для использования переменной, функции, класса, интерфейса и т. д., экспортированной из другого модуля, она должна быть импортирована с помощью одной из форм импорта.
Немодули
Прежде чем начать, важно понять, что TypeScript считает модулем. Спецификация JavaScript заявляет, что любые файлы JavaScript без export или верхнеуровневого объявления await должны рассматриваться как скрипт, а не как модуль.
В файле скрипта переменные и типы объявляются в общей глобальной области видимости, и предполагается, что вы будете либо использовать параметр компилятора outFile для объединения нескольких входных файлов в один выходной файл, либо использовать несколько тегов <script> в вашем HTML для загрузки этих файлов (в правильном порядке!).
Если у вас есть файл, который в настоящее время не содержит import или export, но вы хотите, чтобы он обрабатывался как модуль, добавьте строку:
export {}; Это превратит файл в модуль, не экспортирующий ничего. Этот синтаксис работает независимо от вашего целевого модуля.
Модули в TypeScript
Дополнительное чтение:
Impatient JS (Модули)
MDN: Модули JavaScript
При написании модульного кода на TypeScript следует учитывать три основных момента:
- Синтаксис: Какой синтаксис я хочу использовать для импорта и экспорта элементов?
- Разрешение модулей: Какова взаимосвязь между именами модулей (или путями) и файлами на диске?
- Целевой формат вывода модуля: Как должен выглядеть мой сгенерированный JavaScript-модуль?
Синтаксис ES Модулей
Файл может объявить основной экспорт с помощью export default:
// @filename: hello.ts
export default function helloWorld() {
console.log("Hello, world!");
} Затем он импортируется с помощью:
import helloWorld from "./hello.js"; helloWorld();
Помимо стандартного экспорта, вы можете иметь более одного экспорта переменных и функций с помощью export опуская 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;
} Их можно использовать в другом файле с помощью синтаксиса import:
import { pi, phi, absolute } from "./maths.js";
console.log(pi);
const absPhi = absolute(phi); Дополнительный синтаксис импорта
Импорт можно переименовать, используя формат, подобный import {old as new}:
import { pi as π } from "./maths.js";
console.log(π); Вы можете комбинировать вышеперечисленные синтаксисы в одном import:
// @filename: maths.ts
export const pi = 3.14;
export default class RandomNumberGenerator {}
// @filename: app.ts
import RandomNumberGenerator, { pi as π } from "./maths.js";
RandomNumberGenerator;
console.log(π); Вы можете собрать все экспортированные объекты в единый пространство имен с помощью * as name:
// @filename: app.ts import * as math from "./maths.js"; console.log(math.pi); const positivePhi = math.absolute(math.phi);
Вы можете импортировать файл, не включая какие-либо переменные в свой текущий модуль с помощью import "./file":
// @filename: app.ts
import "./maths.js";
console.log("3.14"); В этом случае import ничего не делает. Однако весь код в maths.ts был выполнен, что могло вызвать побочные эффекты, влияющие на другие объекты.
Специфичный для TypeScript синтаксис ES Модулей
Типы могут быть экспортированы и импортированы с помощью того же синтаксиса, что и JavaScript значения:
// @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 двумя концепциями для объявления импорта типа:
import type
Это оператор импорта, который может импортировать только типы:
// @filename: animal.ts
export type Cat = { breed: string; yearOfBirth: number };
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(); Встроенные type импорты
TypeScript 4.5 также позволяет предварять отдельные импорты префиксом type для указания того, что импортированная ссылка является типом:
// @filename: app.ts
import { createCatName, type Cat, type Dog } from "./animal.js";
export type Animals = Cat | Dog;
const name = createCatName(); Вместе это позволяет не-TypeScript транспайлерам, таким как Babel, swc или esbuild, знать, какие импорты можно безопасно удалить.
Синтаксис ES Модулей с поведением CommonJS
TypeScript имеет синтаксис ES Модулей, который прямо соответствует CommonJS и AMD require. Импорты с помощью ES Модулей в большинстве случаев такие же, как require из этих сред, но этот синтаксис гарантирует 1:1 соответствие в вашем файле TypeScript с выходным CommonJS кодом:
import fs = require("fs");
const code = fs.readFileSync("hello.ts", "utf8"); Подробнее об этом синтаксисе вы можете узнать на странице справочника по модулям по модулям.
Синтаксис CommonJS
CommonJS — это формат, в котором поставляется большинство модулей в npm. Даже если вы пишете с использованием синтаксиса ES Modules, вышеописанное понимание синтаксиса CommonJS поможет вам легче отлаживать код.
Экспорт
Идентификаторы экспортируются путем установки свойства 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,
}; Затем эти файлы можно импортировать с помощью оператора require:
const maths = require("./maths");
maths.pi; Или можно немного упростить, используя функцию деструктуризации в JavaScript:
const { squareTwo } = require("./maths");
squareTwo; Взаимодействие CommonJS и ES Модулей
Между CommonJS и ES Модулями существует несовпадение функций, касающихся различия между импортом по умолчанию и импортом модульного объекта. TypeScript имеет флаг компилятора, позволяющий снизить трение между двумя разными наборами ограничений с помощью esModuleInterop.
Параметры разрешения модулей в TypeScript
Разрешение модулей — это процесс получения строки из операторов import или require и определения, к какому файлу эта строка относится.
TypeScript включает две стратегии разрешения: Классическая и Node. Классическая, по умолчанию, когда параметр компилятора module не commonjs, включена для обратной совместимости. Стратегия Node повторяет работу Node.js в режиме CommonJS, с дополнительными проверками на .ts и .d.ts.
Существует множество флагов TSConfig, влияющих на стратегию модулей в TypeScript: moduleResolution, baseUrl, paths, rootDirs.
Полные сведения о работе этих стратегий вы можете найти в разделе Разрешение модулей.
Параметры вывода модулей в TypeScript
Существует два параметра, влияющих на выходной JavaScript-код:
-
target, определяющий, какие функции JavaScript будут понижены до старых версий (преобразуются для работы в более старых средах выполнения JavaScript) и какие останутся неизменными -
module, определяющий код, используемый для взаимодействия модулей друг с другом
Используемый вами target определяется доступными функциями в среде выполнения JavaScript, в которой вы ожидаете запустить код TypeScript. Это может быть: самый старый поддерживаемый веб-браузер, самая низкая поддерживаемая версия Node.js или уникальные ограничения среды выполнения, такие как Electron.
Все взаимодействие между модулями происходит через загрузчик модулей. Параметр компилятора module определяет, какой загрузчик используется. Во время выполнения загрузчик модулей отвечает за поиск и выполнение всех зависимостей модуля перед его выполнением.
Например, вот файл TypeScript, использующий синтаксис ES Modules, демонстрирующий несколько разных вариантов module:
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;
});
Обратите внимание, что ES2020 фактически эквивалентен исходному
index.ts.
Все доступные параметры и их соответствующий сгенерированный JavaScript-код вы можете посмотреть в справочнике TSConfig для module.
Пространства имён в TypeScript
TypeScript имеет свой собственный формат модулей под названием namespaces, который предшествует стандарту ES Modules. Этот синтаксис имеет множество полезных функций для создания сложных файлов определений и до сих пор активно используется в DefinitelyTyped. Хотя он не устарел, большинство функций пространств имен существуют в ES Modules, и мы рекомендуем использовать его, чтобы соответствовать направлению JavaScript. Более подробную информацию о пространствах имен вы найдёте в справочнике по пространствам имён.
© 2012-2023 Microsoft
Licensed under the Apache License, Version 2.0.
https://www.typescriptlang.org/docs/handbook/2/modules.html