Комментарии 12
Как вы относитесь к переходу от магии декораторов к строгой типизации?
Положительно отношусь. Даже запилил свой DI-контейнер под чистый JS - https://github.com/teqfw/di
Правда, тут JSDoc вместо "строгой типизации", но для навигации по коду этого хватает, а в runtime JSDoc не используется точно так же, как и "строгая типизация" TypeScript. Зато одинаково хорошо работает и для фронта, и для бэка.
На этом фоне у традиционных DI-решений есть три пути, и все три — тупиковые
Есть ещё масса нетрадиционных тупиковых путей. Надо только покопать.
Я, кстати, у себя вообще отказался от container.registerXXX , оставил только для тестового режима. Всё остальное "на магии", как вы говорите - через настройку маппинга пространства имён на пути в файловой системе (калька с Java с их classpath и PHP с PSR-4).
Зато такой подход даёт возможность вообще отказаться от статических импортов (они есть только в Composition Root) и добавить в приложения такие экзотические для JavaScript вещи, как interface.
То, что это, кроме меня, нафиг никому не надо в JS - это отдельный вопрос. Но у меня есть :)
Посмотрел teqfw/di - спасибо интересный подход.
Согласен с вами в одном важном моменте: в runtime TypeScript типы действительно так же отсутствуют, как и JSDoc, но для InferDI ключевая ценность не в runtime, а в том, что TypeScript успевает проверить граф до запуска приложения и запретить некорректный граф на этапе компиляции: неправильный порядок зависимостей, отсутствующий ключ, несовпадение constructor signature, singleton → scoped/transient и т.д.
Ваш подход, насколько я понял, делает ставку на runtime-конвенции и модульный resolver. Это сильная идея для pure JS. InferDI же сознательно идёт в другую сторону, только явный Composition Root, потому что именно он становится типом графа.
Есть интересный пример реализации DI системы - @tinkoff/dippy. Слышали о таком?
Нет, раньше не встречал @tinkoff/dippy, спасибо за пример. Посмотрел — интересная реализация.
Но как раз в рамках статьи я говорю о другом ожидании от DI в 2026 году. TypeScript сегодня может намного больше, чем просто типизировать отдельный token или provider. Поэтому современный DI-контейнер, на мой взгляд, должен использовать всю мощь TypeScript, чтобы давать максимум статических гарантий ещё до запуска приложения.
DI не должен добавлять новые источники ошибок: незарегистрированные зависимости, ошибки в списке constructor-зависимостей, случайные singleton → scoped/request зависимости. Напротив, он должен помогать сводить такие проблемы к минимуму на этапе компиляции.
В этом смысле Dippy не позволяет компилятору проверить корректность всего DI-графа до запуска приложения.
Все игнорируют слона в комнате
Вы решаете проблему доп проверками, валидациями
В $mol объект существует ровно пока от него кто-то зависит
Т.е. проблема 3 у вас невыразима в компиляции, а у $mol невыразима в принципе. Нельзя написать такой ошибочный код
namespace $ {
// singleton: на корневом $, один на процесс
export class $my_metrics extends $mol_object2 {
@ $mol_mem static total() { return 0 }
}
function handle( req: Request ) {
const $$ = Object.create( $ ) as typeof $
$$.$my_request_user = () => req.user // "scoped": живёт только в этом $$
// ...вся работа по запросу идёт через $$...
$.$my_metrics.total() // singleton читает из КОРНЯ $
// req-данных на корневом $ нет → протащить их в singleton нечем
}
}данные текут только в одну сторону, от долгоживущего корня к короткоживущему наследнику, но не обратно.
Да, в $mol действительно сильный архитектурный приём: проблема решается не дополнительными проверками DI-графа, а самой моделью контекста.
Но у этого подхода есть цена: нужно принять целую парадигму $mol: ambient context, реактивные мемоизированные свойства и жизненный цикл объектов через дерево зависимостей. То есть приложение должно быть написано в этой модели.
Поэтому я бы разделил эти подходы так: $mol делает такую ошибку невыразимой через архитектуру всего приложения, а InferDI через тип DI-графа внутри обычной TypeScript-архитектуры. Это разные уровни решения одной и той же проблемы.
А вы видели как di реализован в последнем angular? service = inject(UserService) и это красиво. А вот ваши контейннеры выглядят неочень. К тому же не увидел в InferDi с инжектом интерфейсов?
Да, service = inject(UserService) выглядит лаконично. Однако это именно тот компромисс, о котором я писал в разделе «Магия автосвязывания vs Строгость компилятора». Angular делает разрешение зависимостей неявным и переносит часть рисков в runtime. Если провайдер не зарегистрирован, токен указан неверно или inject() вызван вне контекста инъекции — вы узнаете об этом только в runtime.
В InferDI фокус принципиально другой: приоритетом является не внешняя простота, а проверяемость DI-графа на этапе компиляции TypeScript. Да, зависимости нужно явно описывать в Composition Root и визуально это выглядит менее эффектно, но зато компилятор строго валидирует ключи, типы, порядок аргументов конструктора и lifetime-связи. Это осознанный инженерный выбор в пользу надежности.
API Angular действительно изящнее в месте использования. Но InferDI предлагает другую эстетику — архитектурную надежность и статические гарантий до запуска приложения.
По интерфейсам — в Angular тоже их нельзя использовать как DI token, потому что они исчезают после компиляции. Angular решает это через InjectionToken<T>, а InferDI через аналогичную регистрацию по Symbol('token').
Будущее это Ai разработка. Если спросить у разных Ai построить архитектуру Ai first приложения и какие антипаттерны лучше не использовать то все ai вам скажут что di это зло. До свидания.
Аргумент «все ai вам скажут что di это зло» не является техническим. Если безоговорочно принимать ответы LLM за архитектурные решения, это превращает работу с AI скорее в «религию», чем в инженерный анализ.
На мой взгляд, AI полезен не для перекладывания ответственности за результат, а для поиска вариантов, сравнения компромиссов и проверки решений. Финальное решение всё равно должно опираться на понимание предметной области, требований и рисков.
Тем более в AI-first приложениях зависимости никуда не исчезают. Отказ от DI просто переносит их в глобальные imports, скрытые singleton’ы, service locator, неявное состояние и runtime инициализацию. Архитектура от этого не становится чище.
Поэтому «di это зло» слишком общее утверждение. Плохой DI — зло. Собственно, значительная часть статьи как раз об этом.
Будущее это Ai разработка.
Тут согласен.
Если спросить у разных Ai построить архитектуру Ai first приложения и какие антипаттерны лучше не использовать то все ai вам скажут что di это зло.
Это я не проверял, но уверенно предположу, что это ложь. DI десятилетиями (!!) использовалась "кожанными" в разных ЯП, особенно в "кровавом энтерпрайзе". Это проверенный временем и практикой архитектурный шаблон, на котором, кстати, модели сами учились программировать.
Да, и вообще, ваша LLM ответит вам то, что вы хотите услышать. Они те ещё манипуляторы! Мой GPT-чат от DI в JS в восторге. Вот пример кода, полностью написанного ИИ (Codex-агент), и там DI ¯\_(ツ)_/¯

DI в TypeScript без декораторов: почему это будущее