Ответ 1
Я не понял, как мы классифицируем их?
Пространства имен используются для организации/инкапсуляции кода. Внешние модули используются для организации/инкапсуляции кода и определения местоположения вашего кода во время выполнения. На практике у вас есть два варианта во время выполнения: 1) объединить весь преобразованный код в один файл или 2) использовать внешние модули и иметь несколько файлов и потребовать какой-то другой механизм для доступа к этим файлам.
Когда экспортировать класс или пространство имен или пакет?
Чтобы сделать тип или значение видимым вне файла, в котором он находится, вы должны экспортировать его, если он находится внутри пространства имен. Независимо от того, экспортируете ли вы его на верхнем уровне или в пространстве имен, будет ли он установлен во внешнем модуле.
Если мы экспортируем пакет/пространство имен, все классы внутри них экспортируются или должны быть явно экспортированы
Классы в пространстве имен всегда должны быть явно экспортированы, чтобы класс был видимым во время компиляции вне файла, в котором он определен.
Как каждый из них может быть импортирован/необходим?
Это зависит от того, используете ли вы внешние модули. Внешнему модулю всегда нужно импортировать его для "использования". Импорт пространства имен, который не во внешнем модуле, действительно просто предоставляет псевдоним для пространства имен - вам все равно придется префикс типа/все с псевдонимом (и именно поэтому вы, как правило, не хотите использовать пространства имен с внешними модулями; это означает, что вам всегда нужно использовать префикс при обращении к чему-либо, предоставленному внешним модулем.) Пространства имен, которые не находятся во внешнем модуле, могут охватывать файлы, поэтому, если вы находитесь в одном и том же пространстве имен, вы можете ссылаться на все, что экспортировано пространство имен, не требующее какого-либо импорта.
Чтобы действительно понять вышеизложенное, вам понадобятся некоторые базовые знания. Ключевое значение, которое нужно понимать в ссылках/пространствах имен/внешних модулях, - это то, что эти конструкции делают во время компиляции и что они делают во время выполнения.
Справочные директивы используются во время компиляции для поиска информации о типе. У вашего источника есть определенный символ. Как компилятор TypeScript определяет определение для этого символа? Справочная директива в основном была включена в механизм tsconfig.json - используя tsconfig.json, вы сообщаете компилятору, где находятся все ваши источники.
Пространства имен могут содержать определения типов и/или реализацию. Если пространство имен содержит только информацию о типе, то оно вообще не имеет проявления во время выполнения - вы можете проверить это, посмотрев на вывод JS и найдя пустой файл JS. Если пространство имен имеет код реализации, тогда код завершается внутри замыкания, которое назначается глобальной переменной с тем же именем, что и пространство имен. При вложенных пространствах имен будет глобальная переменная для пространства имен корней. Опять же, проверьте выход JS. Пространства имен исторически являются тем, как клиентские библиотеки JS пытались избежать проблемы с коллизиями имен. Идея состоит в том, чтобы обернуть всю вашу библиотеку в одно закрытие, а затем выявить как можно больший глобальный след - только одну глобальную переменную, ссылающуюся на закрытие. Ну, проблема в том, что вы заявили имя в глобальном пространстве. Что, если вы хотите, скажем, две версии библиотеки? Пространство имен TypeScript по-прежнему содержит вопрос о том, как найти источник для пространства имен. То есть исходный код, который ссылается на A.B, все еще имеет проблему, говорящий компилятору, как найти A.B - либо с помощью ссылочных директив, либо с помощью tsconfig.json. Или путем помещения пространства имен во внешний модуль и затем импорта внешнего модуля.
Внешние модули возникли на стороне сервера JS. Между внешним модулем и файлом файловой системы существует взаимно однозначное соответствие. Вы можете использовать структуру каталогов файловой системы для организации внешних модулей во вложенную структуру. Импорт внешнего модуля, как правило, приводит к зависящей от времени выполнения внешнему модулю (исключение - это когда вы импортируете внешний модуль, но затем не используете какой-либо его экспорт в позиции значения, то есть вы импортируете только внешний модуль чтобы получить информацию о своем типе). Внешний модуль неявно находится в замыкании, и это ключ: пользователь модуля может назначить замыкание любой локальной переменной, которую они хотят. TypeScript/ES6 добавляет дополнительный синтаксис вокруг сопоставления экспорта внешних модулей с локальными именами, но это просто тонкость. На стороне сервера размещение внешнего модуля относительно прямо: просто найдите файл, представляющий внешний модуль в локальной файловой системе. Если вы хотите использовать внешние модули на стороне клиента, в браузере он становится более сложным, так как нет эквивалента файловой системе, у которой есть модуль, доступный для загрузки. Итак, теперь на стороне клиента вам нужен способ объединить все эти файлы в форму, которую можно использовать удаленно в браузере - вот где такие модули, как Webpack (Webpack делает чертовски много больше, чем пакеты модулей) и Browserify вступает в игру. Модульные модули позволяют выполнять во время работы внешние модули в браузере.
Сценарий реального мира: AngularJS. Притвориться, что внешние модули не существуют, используйте единое пространство имен для ограничения загрязнения глобального пространства (в примере ниже одна переменная MyApp - это все, что есть в глобальном пространстве), только экспортные интерфейсы и использование AnggleJS-зависимой инъекции для выполнения реализаций доступный для использования. Поместите все классы в корне каталога, добавьте tsconfig.json в корневой каталог, установите шаблоны angularjs под одним и тем же корнем каталога, чтобы tsconfig.json тоже его подбирал, объединив все выходные данные в один 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);
})();