Я не понял, как мы их классифицируем?
Пространства имен используются для организации / инкапсуляции вашего кода. Внешние модули используются для организации / инкапсуляции вашего кода И для поиска вашего кода во время выполнения. На практике у вас есть два варианта во время выполнения: 1) объединить весь транспилированный код в один файл или 2) использовать внешние модули и иметь несколько файлов и требовать некоторого другого механизма для доступа к этим файлам.
Когда экспортировать класс, пространство имен или пакет?
Чтобы сделать тип или значение видимыми за пределами файла, в котором они находятся, вы должны экспортировать их, если они находятся внутри пространства имен. Независимо от того, экспортируете ли вы его на верхнем уровне или в пространстве имен, будет зависеть, находится ли он сейчас во внешнем модуле.
Если мы экспортируем пакет / пространство имен, все классы внутри него экспортируются или должны быть экспортированы явно.
Классы в пространстве имен всегда должны быть экспортированы явно, чтобы класс был виден во время компиляции за пределами файла, в котором он определен.
Как каждый из них можно импортировать / потребовать?
Это зависит от того, используете ли вы внешние модули. Внешний модуль всегда нужно будет импортировать, чтобы «использовать» его. Импорт пространства имен, которое не находится во внешнем модуле, на самом деле просто предоставляет псевдоним для пространства имен - вам все равно нужно префикс типа / любого другого псевдонимом (и именно поэтому вы обычно не хотите использовать пространства имен с внешними модулями; это означает, что вы всегда должны использовать префикс при ссылке на что-либо, предоставленное внешним модулем.) Пространства имен, которых нет во внешнем модуле, могут охватывать файлы, поэтому, если вы находитесь в том же пространстве имен, вы можете ссылаться на все, что экспортировано пространство имен без необходимости какого-либо импорта.
Чтобы действительно понять вышесказанное, вам потребуются некоторые базовые знания. Ключевым моментом, который нужно понимать со ссылками / пространствами имен / внешними модулями, является то, что эти конструкции делают во время компиляции и что они делают во время выполнения.
Справочные директивы используются во время компиляции для поиска информации о типе. В вашем источнике есть особый символ. Как компилятор TypeScript находит определение для этого символа? Ссылочная директива в значительной степени была включена в механизм tsconfig.json - используя tsconfig.json, вы сообщаете компилятору, где находятся все ваши источники.
Пространства имен могут содержать определения типов и / или реализацию. Если пространство имен содержит только информацию о типе, тогда оно вообще не имеет проявления во время выполнения - вы можете проверить это, посмотрев на вывод JS и найдя пустой файл JS. Если пространство имен имеет код реализации, то код помещается в закрытие, которое назначается глобальной переменной с тем же именем, что и пространство имен. Во вложенных пространствах имен для корневого пространства имен будет глобальная переменная. Опять же, проверьте вывод JS. Пространства имен исторически представляют собой то, как клиентские библиотеки JS пытались избежать проблемы с конфликтами имен. Идея состоит в том, чтобы обернуть всю вашу библиотеку в одно закрытие, а затем предоставить как можно меньший глобальный след - только одну глобальную переменную, ссылающуюся на закрытие. Что ж, проблема по-прежнему в том, что вы заявили о себе в глобальном пространстве. Что, если вам нужны, скажем, две версии библиотеки? Для пространства имен TypeScript все еще существует проблема, как найти источник для пространства имен. То есть исходный код, который ссылается на AB, по-прежнему имеет проблему с сообщением компилятору, как найти AB - либо с помощью ссылочных директив, либо с помощью tsconfig.json. Или поместив пространство имен во внешний модуль, а затем импортировав внешний модуль.
Внешние модули созданы на стороне сервера JS. Между внешним модулем и файлом в файловой системе существует взаимно однозначное соответствие. Вы можете использовать структуру каталогов файловой системы для организации внешних модулей во вложенную структуру. Импорт внешнего модуля, как правило, всегда вводит зависимость времени выполнения от этого внешнего модуля (исключение - когда вы импортируете внешний модуль, но затем не используете его экспорт в позиции значения - то есть вы импортируете только внешний модуль чтобы получить информацию о его типе). Внешний модуль неявно находится в замыкании, и это ключевой момент: пользователь модуля может назначить замыкание любой локальной переменной, которую он хочет. TypeScript / ES6 добавляет дополнительный синтаксис для сопоставления экспорта внешних модулей с локальными именами, но это всего лишь тонкость. На стороне сервера найти внешний модуль относительно просто: просто найдите файл, представляющий внешний модуль, в локальной файловой системе. Если вы хотите использовать внешние модули на стороне клиента, в браузере это становится более сложным, поскольку нет эквивалента файловой системе, в которой модуль доступен для загрузки. Итак, теперь на стороне клиента вам нужен способ объединить все эти файлы в форму, которая может использоваться удаленно в браузере - именно здесь бандлеры модулей, такие как Webpack (хотя Webpack делает намного больше, чем объединение модулей) и Browserify вступает в игру. Сборщики модулей позволяют разрешать внешние модули во время выполнения в браузере. в браузере он становится более сложным, поскольку нет эквивалента файловой системе, в которой модуль доступен для загрузки. Итак, теперь на стороне клиента вам нужен способ объединить все эти файлы в форму, которая может использоваться удаленно в браузере - именно здесь бандлеры модулей, такие как Webpack (хотя Webpack делает намного больше, чем объединение модулей) и Browserify вступает в игру. Сборщики модулей позволяют разрешать внешние модули во время выполнения в браузере. в браузере он становится более сложным, поскольку нет эквивалента файловой системе, в которой модуль доступен для загрузки. Итак, теперь на стороне клиента вам нужен способ объединить все эти файлы в форму, которую можно будет использовать удаленно в браузере - именно здесь бандлеры модулей, такие как Webpack (хотя Webpack делает намного больше, чем объединение модулей) и Browserify вступает в игру. Сборщики модулей позволяют разрешать внешние модули во время выполнения в браузере.
Сценарий реального мира: AngularJS. Представьте, что внешних модулей не существует, используйте единое пространство имен для ограничения загрязнения глобального пространства (в приведенном ниже примере единственная переменная MyApp - это все, что находится в глобальном пространстве), экспортируйте только интерфейсы и используйте внедрение зависимостей AngularJS для реализации доступны для использования. Поместите все классы в корень каталога, добавьте tsconfig.json в корень, установите angularjs typings в том же корне каталога, чтобы tsconfig.json тоже взял его, объедините весь вывод int в один JS файл. Это будет нормально работать для большинства проектов, если повторное использование кода не вызывает больших проблем.
MyService.ts:
namespace MyApp {
// without an export the interface is not visible outside of MyService.ts
export interface MyService {
....
}
// class is not exported; AngularJS DI will wire up the implementation
class MyServiceImpl implements MyService {
}
angular.module("MyApp").service("myService", MyServiceImpl);
}
MyController.ts:
namespace MyApp {
class MyController {
// No import of MyService is needed as we are spanning
// one namespace with multiple files.
// MyService is only used at compile time for type checking.
// AngularJS DI is done on the name of the variable.
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
}
Использование IIFE во избежание загрязнения глобальной среды выполнения. В этом примере вообще не создаются глобальные переменные. (Предполагается, что tsconfig.json.)
Foo.ts:
namespace Foo {
// without an export IFoo is not visible. No JS is generated here
// as we are only defining a type.
export interface IFoo {
x: string;
}
}
interface ITopLevel {
z: string;
}
(function(){
// export required above to make IFoo visible as we are not in the Foo namespace
class Foo1 implements Foo.IFoo {
x: string = "abc";
}
// do something with Foo1 like register it with a DI system
})();
Bar.ts:
// alias import; no external module created
import IFoo = Foo.IFoo;
(function() {
// Namespace Foo is always visible as it was defined at
// top level (outside of any other namespace).
class Bar1 implements Foo.IFoo {
x: string;
}
// equivalent to above
class Bar2 implements IFoo {
x: string;
}
// IToplevel is visible here for the same reason namespace Foo is visible
class MyToplevel implements ITopLevel {
z: string;
}
})();
Используя IIFE, вы можете отказаться от использования MyApp в качестве глобальной переменной в первом примере.
MyService.ts:
interface MyService {
....
}
(function() {
class MyServiceImpl implements MyService {
}
angular.module("MyApp").service("myService", MyServiceImpl);
})();
MyController.ts:
(function() {
class MyController {
constructor(private myService: MyService) {
}
}
angular.module("MyApp").controller("myController", MyController);
})();