Как использовать ссылки на проекты в TypeScript 3.0?

В TypeScript 3.0 появилась новая функция, которая называется Ссылки проекта. Это предполагает лучшее взаимодействие модулей *.ts между собой. К сожалению, это все, что я мог получить из официальной документации, хотя она написана довольно четко и прямо.

Может ли кто-нибудь помочь мне точно понять, какие проблемы он решает, как он это делает и как я могу от этого выиграть? У меня есть проект с похожей структурой, поэтому он может (или не может) быть очень полезным для него. Заранее спасибо!


UPD: структура проекта примерно такая:

project/
    lib/
        index.ts # defines the original code
    test/
        index.spec.ts # requires lib/index.ts
    package.json
    tsconfig.json

Ответ 1

Since the question gets more and more upvotes, I dug into it and managed to understand the feature.
Hope this answer is helpful.


TL; DR:

Эта функция позволяет определять части проекта как отдельные модули TypeScript. Помимо прочего, это позволяет по-разному настраивать эти модули, создавать их отдельно и т.д.


Перед

Изначально структура проекта в упрощенном виде выглядит следующим образом:

/
    src/
        entity.ts # exports an entity
    test/
        entity.spec.ts # imports an entity
    tsconfig.json

Сущность определяется в модуле src/entity.ts, а затем используется в файле test/entity.spec.ts.

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

Эта структура, однако, создает проблему: процесс компиляции проекта (а именно, tsc) также компилирует тестовые файлы, создавая таким образом файлы dist/test/entity.spec.{js|d.ts} в выходных данных. Этого не должно быть, поэтому файл tsconfig.json слегка изменен и включает в себя только те файлы/папки, которые предназначены для внешнего использования:

{
    "compilerOptions": {
        // compiler options
    },
    "include": [
        "./src"
    ]
}

Это решает проблему, но в моем случае это также привело к тому, что все файлы в папке /test время от времени игнорируются компилятором TypeScript в процессе разработки. Кроме того, этот эксклюзивный подход может не подходить для всех.


После

После использования функции структура проекта изменилась на следующую:

/
    src/
        entity.ts # exports an entity
        tsconfig.json
    test/
        entity.spec.ts # imports an entity
        tsconfig.json
    tsconfig-base.json

Отпустите изменения:

  1. Переименование /tsconfig.json в /tsconfig-base.json само по себе является довольно важной вещью: корневая папка больше не является проектом TypeScript, так как tsc требует наличия файла tsconfig.json.
  2. С другой стороны, добавление файлов src/tsconfig.json и test/tsconfig.json превращает и src, и test в два отдельных проекта TypeScript, независимых друг от друга.

Содержимое файлов /{src|test}/tsconfig.json схоже, так как никаких изменений в конфигурации не ожидалось, то есть "строгость", папка вывода, а также другие подобные параметры должны быть сохранены. Чтобы сделать их похожими, не вставляя при этом копий, все конфигурации помещаются в произвольный файл, доступный из обоих мест; в этом случае для этого был выбран tsconfig-base.json в корневой папке:

// the contents of /tsconfig-base.json
{
    "compilerOptions": {
        // compiler options, common to both projects
    }
}

Этот файл "наследуется", затем файлами /{src|test}/tsconfig.json, с добавлением любых других опций, если это необходимо:

// the contents of /{src|test}/tsconfig.json
{
    "extends": "../tsconfig-base.json",
    "compilerOptions": {
        // additional compiler options, specific to a project
    }
}

Обратите внимание, как этот шаблон похож на определение abstract class с неполной реализацией, а затем расширение его двумя отдельными "конкретными" классами.

Теперь папки /src и /test в основном содержат два отдельных проекта TypeScript с похожей конфигурацией. Последнее, что нужно сделать, это указать связь между ними. Поскольку test зависит от src, test должен каким-то образом "знать" о src. Это делается в два довольно очевидных шага:

Массив "include" в /tsconfig-base.json теперь не нужен, поскольку исключение из кода выполняется путем "рисования новых границ".

Теперь для проекта test требуются файлы *.d.ts для проекта src. Это означает, что перед запуском тестов src должен быть собран отдельно. Это делается с помощью нового режима tsc, запускаемого опцией --build:

tsc --build src

Эта команда создает проект src и помещает выходные данные в указанную выходную папку (в данном случае /dist), не прерывая работу test и не теряя ошибок компиляции.

Ответ 2

это для машинописных библиотек, которые вы разрабатываете, которые используются другим приложением типов. Например, если вы используете некоторую библиотеку- lodash как lodash но активно ее разрабатываете вместе со своим зависимым приложением, references в "tsconfig.json" позволяют ссылаться на исходный код и автоматически перестраивать зависимое приложение, когда изменения источника использования (IE: tsc обнаруживает изменения исходного кода в утилите ts lib)

В моем случае я использую references в сочетании с npm link и git submodules и он работает намного лучше, чем в ts 2.x

Ответ 3

**** ОБНОВЛЕНИЕ **** (3 августа 2018 года)

Дайте кредит, где должен быть кредит.

На приведенный ниже контент ссылались на " Announcing TypeScript 3.0 " Даниэля Розенвассера из Microsoft.


Эта проблема

Его довольно распространенный способ иметь несколько разных этапов сборки для библиотеки или приложения. Возможно, ваша кодовая база имеет src и test каталог. Возможно, у вас есть свой интерфейсный код в папке с client с вашим внутренним кодом Node.js в папке с именем server и каждый импортирует код из shared папки. И, может быть, вы используете так называемый "monorepo" и имеете много проектов, которые зависят друг от друга нетривиальными способами.

Ссылки на проекты

Ссылки на проекты направлены на облегчение работы с этими сценариями.

Ссылки на проект позволяют проектам TypeScript зависеть от других проектов типа TypeScript, в частности, позволяя файлам tsconfig.json ссылаться на другие файлы tsconfig.json. Задание этих зависимостей упрощает разделение вашего кода на более мелкие проекты, поскольку он дает TypeScript (и инструменты вокруг него) способ понять структуру упорядочения и вывода данных. Это означает, что такие вещи, как быстрые сборки, работают постепенно и поддерживают прозрачную навигацию, редактирование и рефакторинг по проектам.

На что это похоже?

В качестве краткого примера, как tsconfig.json с проектами, выглядит так:

// ./src/bar/tsconfig.json
{
    "compilerOptions": {
        // Needed for project references.
        "composite": true,
        "declaration": true,

        // Other options...
        "outDir": "../../lib/bar",
        "strict": true, "module": "esnext", "moduleResolution": "node",
    },
    "references": [
        { "path": "../foo" }
    ]
}

Здесь есть два новых поля: composite и references.

references просто указывают другие файлы tsconfig.json (или папки, содержащие их сразу). Каждая ссылка в настоящее время представляет собой только объект с полем path и позволяет TypeScript знать, что для создания текущего проекта требуется сначала создать проект, на который ссылается проект.

Возможно, столь же важным является composite поле. В composite поле предусмотрены определенные опции, позволяющие ссылаться на этот проект и строить его поэтапно для любого проекта, который зависит от него. Важное значение имеет способность к разумной и постепенной перестройке, поскольку скорость сборки является одной из причин, по которой вы можете разбить проект в первую очередь. Например, если проект front-end зависит от shared, и shared зависит от core, наш API, вокруг ссылок проекта может быть использован для обнаружения изменений в core, но только перестроение shared, если типы (т.е..d.ts файлы) произведенные core, изменились. Это означает, что изменение core не полностью заставляет нас восстанавливать мир. По этой причине установка composite сил также устанавливает флаг declaration.

Режим сборки для TypeScript

TypeScript 3.0 предоставит набор API для ссылок на проекты, чтобы другие инструменты могли обеспечить это быстрое постепенное поведение. tsc теперь поставляется с новым --build флагом.

tsc --build (или его псевдоним, tsc -b) выполняет набор проектов и строит их и их зависимости.

Управление структурой вывода

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

Если вы когда-либо пытались разделить код TypeScript между клиентом и сервером приложения, возможно, вы столкнулись с проблемами управления структурой вывода.

Например, если client/index.ts и server/index.ts ссылаются на shared/index.ts для следующих проектов:

src
├── client
│   ├── index.ts
│   └── tsconfig.json
├── server
│   ├── index.ts
│   └── tsconfig.json
└── shared
    └── index.ts

... затем пытается создать client и server, ну в итоге...

lib
├── client
│   ├── client
│   │   └── index.js
│   └── shared
│       └── index.js
└── server
    ├── server
    │   └── index.js
    └── shared
        └── index.js

скорее, чем

lib
├── client
│   └── index.js
├── shared
│   └── index.js
└── server
    └── index.js

Обратите внимание, что мы закончили с копией shared как на client и на server. Мы без лишних затрат потратили время на shared дважды и внесли нежелательный уровень вложенности в lib/client/client и lib/server/server.

В идеале, TypeScript понимает, что эти файлы не нужно создавать в одной и той же компиляции, а вместо этого переходить к файлам .d.ts для информации о типе.

Создание tsconfig.json для shared использования и использования ссылок на проекты делает именно это. Он сигнализирует TypeScript, что

  1. shared должны быть построены независимо, а
  2. при импорте из ../shared, мы должны искать файлы .d.ts в его выходном каталоге.

Это позволяет избежать двойного -b uild, а также позволяет избежать случайного поглощения всего содержимого shared.