Skip to content

Типобезопасность

Главное правило InferDI: граф зависимостей живёт в системе типов. Неверный граф - перепутанный порядок аргументов, незарегистрированный ключ, singleton, который тянется к scoped-состоянию - это ошибка типа прямо в редакторе, а не stack trace, который вы найдёте под нагрузкой. Всё, что компилятор может доказать статически, проверяется статически; runtime-защита нужна только для того, что проскользнуло через as-касты и динамические ключи.

Сигнатуры конструкторов

registerClass проверяет кортеж зависимостей по параметрам конструктора.

ts
class Logger {}
class Db {}

class UserRepo {
  constructor(logger: Logger, db: Db) {}
}

new Container()
  .registerClass('logger', Logger, [])
  .registerClass('db', Db, [])
  .registerClass('users', UserRepo, ['logger', 'db'])

Если конструктор изменится, регистрация должна измениться вместе с ним. Кортеж ['db', 'logger'] будет отклонён, потому что первый параметр ожидает Logger.

Уникальность ключей

Каждая регистрация возвращает расширенный тип контейнера. Повторная регистрация того же ключа через fluent API отклоняется:

ts
new Container()
  .registerValue('dsn', 'postgres://localhost/app')
  .registerValue('dsn', 'sqlite://memory')

В тестах для намеренной замены используется .override().

Время жизни в типе

Каждая запись хранит тип значения и вид времени жизни. Система типов фильтрует зависимости так, чтобы singleton не мог напрямую зависеть от scoped- или transient-сервисов.

ts
new Container()
  .registerClass('request', RequestContext, [], 'scoped')
  .registerClass('users', UserService, ['request'], 'singleton')

Runtime-проверки в strict mode остаются вторым рубежом защиты для as-кастов, динамических ключей, захваченных внешних контейнеров и циклов зависимостей.