Модули и пространства имен: Каков правильный способ организации большого проекта typescript?

Я новичок в typescript, и я пишу небольшую основу для создания прототипов для WebGl. В настоящее время я реорганизую свой проект и сталкиваюсь с некоторыми проблемами, как организовать мой проект, поскольку оба подхода (модули и пространства имен), похоже, имеют серьезные недостатки.

Этот пост не о том, КАК использовать эти шаблоны, но как преодолеть проблемы, которые каждый из них приносит.

Status Quo: использование пространств имен

Из С# это казалось самым естественным способом. Каждый класс/модуль получает соответствующее пространство имён, и я предоставляю параметр "outFile" в tsconfig.json, поэтому все конкатенируется в один большой файл. После компиляции у меня есть мое корневое пространство имен как глобальный объект. Зависимости не встроены в проект, поэтому вам необходимо вручную предоставить необходимые *.js файлы в html (не хорошо)

Пример файла

namespace Cross.Eye {
    export class SpriteFont {   
        //code omitted
    }    
}

Пример использования (вы должны убедиться, что пространство имен Cross загружено в глобальное пространство имен, прежде чем отправить файл js в html)

namespace Examples {
    export class _01_BasicQuad {
        context: Cross.Eye.Context;
        shader: Cross.Eye.ShaderProgram;

        //code omitted
    }
}

Pros

  • Прямо для использования, если вы отправляетесь с С#/Java
  • Независимо от имени файла - переименование файлов не нарушит ваш код.
  • Простота рефакторинга: IDE могут легко переименовать пространства имен/классы, и изменения будут применяться последовательно для вашего кода.
  • Удобство. Добавление класса в проект так же просто, как добавление файла и объявление его в нужном пространстве имен.

Против

Для большинства проектов мы рекомендуем использовать внешние модули и использовать пространство имен для быстрых демонстраций и портирования старого кода JavaScript.

из https://basarat.gitbooks.io/typescript/content/docs/project/namespaces.html

  • Корневое пространство имен всегда (?) - глобальный объект (плохой)
  • Невозможно (?) использовать такие инструменты, как browserify или webpack, что необходимо для связывания lib с его зависимостями или связывания вашего пользовательского кода с lib при его фактическом использовании.
  • Плохая практика, если вы планируете выпустить модуль npm

Уровень техники (?): Модули

Typescript поддерживает модули ES6, они новые и блестящие, и все, похоже, согласны с тем, что они - путь. Идея состоит в том, что каждый файл является модулем, и, подавая файлы в операторах импорта, вы можете четко определить свои зависимости, что упрощает сбор инструментов для эффективного пакета кода. У меня в основном есть один класс для каждого файла, который, похоже, не работает с шаблоном модуля dhte.

Это моя файловая структура после рефакторе:

enter image description here

Также у меня есть файл index.ts в каждой папке, поэтому я могу импортировать все его классы import * as FolderModule from "./folder"

export * from "./AggregateLoader";
export * from "./ImageLoader";
export * from "./TiledLoader";
export * from "./XhrLoaders";
export * from "./XmlSpriteFontLoader";

Пример файла - я думаю, проблема становится ясно видна здесь.

import {SpriteFont} from "./SpriteFont";
import {ISpriteTextGlyph, ISpriteChar} from "./Interfaces";
import {Event,EventArgs} from "../../Core";
import {Attribute, AttributeConfiguration} from "../Attributes";
import {DataType} from "../GlEnums";
import {VertexStore} from "../VertexStore";
import {IRectangle} from "../Geometry";
import {vec3} from "gl-matrix";

export class SpriteText {
    // code omitted
}

Пример использования. Как вы можете видеть, мне больше не нужно проходить через пространства имен, потому что я могу напрямую импортировать классы.

import {
    Context,
    Shader,
    ShaderProgram,
    Attribute,
    AttributeConfiguration,
    VertexStore,
    ShaderType,
    VertexBuffer,
    PrimitiveType
} from "../cross/src/Eye";

import {
    Assets,
    TextLoader
} from "../cross/src/Load";

export class _01_BasicQuad {
    context: Context;
    shader: ShaderProgram;

    // code omitted.
}

Pros

  • Делает ваш код более модульным, поскольку он больше не привязан к пространствам имен.
  • Вы можете использовать инструменты для объединения, такие как browserfy или webpack, которые также могут связывать все ваши зависимости
  • Вы более гибки при импорте классов и больше не должны перемещаться по цепочкам пространства имен.

Против

  • Очень утомительно, если каждый класс является другим файлом, вам придется вводить одни и те же операторы импорта снова и снова.
  • Переименование файлов нарушит ваш код (плохой).
  • Имена классов рефакторинга не будут распространяться на ваш импорт (очень плохо - может зависеть от вашей среды IDE, но я использую vs-code)

ИМО оба подхода кажутся ошибочными. Пространства имен кажутся ужасно устаревшими, непрактичными для больших проектов и несовместимыми с общими инструментами при использовании модулей, являются довольно неудобными и ломают некоторые из возможностей, которые я адаптировал для typescript в первую очередь.

В идеальном мире я бы написал свою фреймворк с использованием шаблона пространства имен и экспортировал его как модуль, который затем можно импортировать и связывать с его зависимостями. Однако это не представляется возможным без каких-либо уродливых хаков.

Итак, вот мой вопрос: как вы справлялись с этими проблемами? Как можно свести к минимуму недостатки, которые подразумевает каждый подход?

Update

Получив немного больше опыта разработки typescript и javascript в целом, я должен указать, что модули, вероятно, подходят для 90% всех случаев использования.

Последние 10%, как мы надеемся, являются устаревшими проектами, использующими глобальные пространства имен, которые вы хотите оживить с помощью небольшого typescript (который отлично работает, кстати).

Большая часть моего критика для модулей может быть (и была) решена благодаря лучшей поддержке IDE. Visual Studio Code с тех пор добавляет автоматическое разрешение модуля, которое отлично работает.

Ответы

Ответ 1

tl; dr: Не выбирайте прошлое. Выберите будущее: Модули.

В ранних проектах спецификации модулей ES6 существовало понятие встроенных модулей, которое затем была выпущена в июле 2014 года без встроенных модулей. Год спустя, в июле 2015 года, с версией 1.5 из TypeScript, внутренние модули были переименованы в пространства имен во избежание путаницы со стандартом.

Пространства имен являются устаревшей функцией. Он не будет частью языка ECMAScript. И команда TypeScript будет продолжать следовать стандарту. Не было улучшения относительно пространств имен TS с момента выпуска стандарта модулей ECMAScript в июле 2014 года.

Минусы [модулей ES6]

  • Очень утомительно, если каждый класс является другим файлом, вам придется вводить одни и те же операторы импорта снова и снова.
  • Переименование файлов нарушит ваш код (плохой).
  • Имена классов рефакторинга не будут распространяться на ваш импорт (очень плохо - может зависеть от вашей среды IDE, но я использую vs-code)

Мы можем надеяться на некоторые улучшения по этим вопросам с будущими IDE. Первый из них уже разрешен WebStorm.