Вместо того, чтобы иметь дело с ручным созданием зависимостей классов каждый раз, когда мы хотим использовать конкретный класс. Мы могли бы настроить механизм, который мог бы создавать их для нас и автоматически предоставлять зависимости классу. Такой механизм называется контейнером инверсии управления (IoC), и в этом посте я хотел бы показать, как вы можете улучшить свой код TypeScript, настроив контейнер зависимостей с помощью InversifyJS.
Настройка проекта перед использованием внедрения зависимостей (DI)
Я собираюсь показать вам пример, основанный на демонстрационном проекте узла, который состоит из класса обслуживания, зависящего от двух других классов, и основного файла TypeScript, который использует этот сервис. Структура проекта похожа на
src/ dependencies.ts service.ts main.ts
И вы также можете увидеть это на GitHub https://github.com/AndrejsAbrickis/ts-inversify-blog
dependencies.ts
Этот файл TypeScript экспортирует два класса, которые ведут себя одинаково. У обоих есть личное поле name, представляющее имя класса. И у обоих есть частный метод getName(), который возвращает имя класса.

service.ts
Все, что делает класс обслуживания, - это возвращает массив имен зависимостей, которые он использует. Поскольку мы не можем напрямую получить доступ к полю имени только для чтения в классах, мы должны использовать метод экземпляра getName. И чтобы использовать метод, мы должны создать экземпляры классов с помощью ключевого слова new

main.ts
При выполнении основным файлом является «создание нового экземпляра класса» (создание нового экземпляра класса вручную) класса обслуживания, вызов его метода getAllNames и запись результата метода.

Как вы видели, во время разработки мы несколько раз использовали ключевое слово new. А это просто банальный пример сервиса и двух зависимостей. Представьте, что вы делаете это в реальном проекте с десятками и сотнями классов.
Добавить в проект инверсайн
Чтобы реализовать DI в проекте, я собираюсь использовать InversifyJS в качестве контейнера IoC (инверсия элемента управления).
Для начала нам нужно добавить в проект inversify и reflect-metadata.
yarn add -D inversify reflect-metadata
Во-вторых: обновите tsconfig.json, добавив дополнительные настройки в раздел compilerOptions

Настройка проекта после использования внедрения зависимостей (DI)
После установки InversifyJS и настройки компилятора TypeScript для поддержки InversifyJS мы можем обновить код нашего приложения.
di-container.ts
Прежде чем наслаждаться сладкими плодами внедрения зависимостей, мы должны настроить контейнер IoC, чтобы классы могли разрешать свои собственные зависимости из централизованного контейнера.
Мы делаем это, создавая новый контейнер инверсии и предоставляя ему привязки классов. Привязки позволяют контейнеру сопоставлять запрошенную зависимость с ее экземпляром.
В этом примере я использую привязку toSelf() для классов DependencyA и DependencyB. Что дает указание контейнеру возвращать экземпляр класса всякий раз, когда запрашивается (вводится) конкретный класс.

dependencies.ts
После настройки контейнера зависимости можно сделать инъекционными, импортировав injectable декоратор из инверсии и украсив классы @injectable декораторами. Этот декоратор будет обработан и применен к выводу JavaScript с помощью пакета reflect-metadata во время компиляции TypeScript.

service.ts
Теперь ключевое слово new можно удалить из класса обслуживания. И зависимости могут быть вставлены прямо в конструктор класса с помощью декоратора @inject.

В следующем примере будет извлечен экземпляр класса DependencyA из контейнера IoC и передан конструктору в качестве параметра dependencyA.
@inject(DependencyA) dependencyA: DependencyA
main.ts
Сначала обновите импорт, добавив import 'reflect-metadata';. Inversify требует применения декораторов @inject() и @injectable к скомпилированному выводу приложения.
После этого мы можем импортировать контейнер для приложения, а new keyword можно удалить из файла main.ts, изменив объявление service на
const service: Service = DIContainer.resolve<Service>(Service);
Теперь нам больше не нужно вручную создавать Service зависимости классов, поскольку Контейнер будет делать это за нас всякий раз, когда ему будет предложено разрешить Service класс.

Заключение
Внедрение зависимостей - это шаблон, который снимает ответственность за создание зависимостей классов вручную каждый раз, когда мы хотим использовать конкретный класс. Вместо этого мы настраиваем контейнер Inversion of Control (IoC), чтобы сделать это за нас.
Основные преимущества, которые я вижу в этом шаблоне, заключаются в том, что мы можем имитировать и подставлять конкретный экземпляр зависимости. Таким образом, мы можем упростить написание тестов для поведения нашего класса без необходимости вручную создавать все зависимости. А с помощью интерфейсов и контейнера IoC мы можем сделать наш код более расширяемым.
Вы можете увидеть обе реализации befora DI и after DI на Github https://github.com/AndrejsAbrickis/ts-inversify-blog. Чтобы увидеть скомпилированный TypeScript, клонируйте репо, запустите
yarn && yarn build
or
npm install && npm build
и посмотрите каталог ./dist для вывода.
Ваше здоровье!
Если вы нашли этот пост полезным и хотели бы узнать больше о случайных темах веб-разработки, просто похлопайте по этой статье или оставьте здесь комментарий. И, как всегда, вы можете найти меня в Twitter @ andrejsabrickis
Прежде чем я уйду, коротко скажу, что мы нанимаем. В настоящее время наша компания ищет креативных и непредубежденных инженеров-программистов, которые помогут нам вырасти на лидирующем рынке одноранговых инвестиций. Если вам интересно или вы знаете кого-то, кто был бы заинтересован, не стесняйтесь обращаться ко мне лично в Twitter @andrejsabrickis. Также ознакомьтесь с нашими текущими вакансиями на Mintos.
Ура!
/ Андрейс /