Пространства имен и модули
В этой статье описаны различные способы организации кода с помощью модулей и пространств имен в TypeScript. Мы также рассмотрим некоторые продвинутые темы по использованию пространств имен и модулей и коснемся некоторых распространенных ошибок при их использовании в TypeScript.
См. документацию по Модулям для получения дополнительной информации об ES-модулях. См. документацию по Пространствам имен для получения дополнительной информации о пространствах имен TypeScript.
Примечание: в очень старых версиях TypeScript пространства имен назывались «Внутренними модулями»; они предшествуют системам модулей JavaScript.
Использование модулей
Модули могут содержать как код, так и объявления.
Модули также зависят от загрузчика модулей (такого как CommonJs/Require.js) или среды выполнения, поддерживающей ES-модули. Модули обеспечивают лучшую повторную используемость кода, более сильную изоляцию и лучшую поддержку инструментов для объединения.
Также следует отметить, что для приложений Node.js модули являются по умолчанию, и мы рекомендуем использовать модули вместо пространств имен в современном коде.
Начиная с ECMAScript 2015, модули являются встроенной частью языка и должны поддерживаться всеми реализациями совместимых движков. Таким образом, для новых проектов модули будут рекомендуемым механизмом организации кода.
Использование пространств имен
Пространства имен — это специфичный для TypeScript способ организации кода.
Пространства имен — это просто именованные объекты JavaScript в глобальном пространстве имен. Это делает пространства имен очень простым конструктом в использовании. В отличие от модулей, они могут охватывать несколько файлов и могут быть объединены с помощью outFile. Пространства имен могут быть хорошим способом структурировать ваш код в веб-приложении, с включением всех зависимостей в виде тегов <script> в вашем HTML-странице.
Как и при любой глобальной загрязнении пространства имен, трудно определить зависимости компонентов, особенно в большом приложении.
Недостатки пространств имен и модулей
В этом разделе мы опишем различные распространенные недостатки использования пространств имен и модулей и способы их предотвращения.
/// <reference>-ия модуля
Распространённая ошибка — попытка использовать синтаксис /// <reference ... /> для ссылки на файл модуля вместо использования инструкции import. Чтобы понять разницу, нам сначала нужно понять, как компилятор может найти информацию о типе для модуля на основе пути к файлу import (например, путь к файлу ... в import x from "...";, import x = require("...");, и т. д.).
Компилятор будет пытаться найти .ts, .tsx, а затем .d.ts с соответствующим путём. Если определённый файл не найден, компилятор будет искать *декларацию модуля с указанием среды*. Обратите внимание, что эти декларации необходимо объявлять в файле .d.ts.
-
myModules.d.ts// In a .d.ts file or .ts file that is not a module: declare module "SomeModule" { export function fn(): string; } -
myOtherModule.ts/// <reference path="myModules.d.ts" /> import * as m from "SomeModule";
Тег ссылки позволяет нам найти файл объявления, содержащий объявление для модуля со значением по умолчанию. Именно так используется файл node.d.ts , который используется в нескольких примерах TypeScript.
Излишнее использование пространств имен
При преобразовании программы из пространств имен в модули легко получить файл, который выглядит так:
-
shapes.tsexport namespace Shapes { export class Triangle { /* ... */ } export class Square { /* ... */ } }
В этом случае пространство имен верхнего уровня Shapes оборачивает Triangle и Square без причины. Это запутывает и раздражает потребителей вашего модуля:
-
shapeConsumer.tsimport * as shapes from "./shapes"; let t = new shapes.Shapes.Triangle(); // shapes.Shapes?
Ключевая особенность модулей в TypeScript заключается в том, что два разных модуля никогда не вносят имена в одну и ту же область видимости. Поскольку потребитель модуля решает, какое имя присвоить модулю, нет необходимости заранее оборачивать экспортируемые символы в пространство имен.
Ещё раз, почему не стоит пытаться использовать пространство имен для содержимого модуля: основная идея использования пространств имен — обеспечить логическое группирование конструкций и предотвратить конфликты имён. Поскольку сам файл модуля уже является логической группировкой, а его имя верхнего уровня определяется кодом, который его импортирует, нет необходимости использовать дополнительный уровень модуля для экспортируемых объектов.
Вот пересмотренный пример:
-
shapes.tsexport class Triangle { /* ... */ } export class Square { /* ... */ } -
shapeConsumer.tsimport * as shapes from "./shapes"; let t = new shapes.Triangle();
Компромиссы при использовании модулей
Так же, как существует взаимно однозначное соответствие между файлами JS и модулями, TypeScript имеет взаимно однозначное соответствие между файлами исходного кода модуля и генерируемыми файлами JS. Одним из последствий этого является то, что нельзя объединять несколько файлов исходного кода модулей в зависимости от системы модулей, к которой вы обращаетесь. Например, вы не можете использовать опцию outFile при использовании целевых систем commonjs или umd, но с TypeScript 1.8 и более поздними версиями, возможно использование outFile при использовании целевых систем amd или system.
© 2012-2023 Microsoft
Licensed under the Apache License, Version 2.0.
https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html