Skip to content

类型安全

InferDI 的核心原则:依赖图存在于类型系统之中。一个无效的依赖图——错误的参数顺序、从未注册的键、单例去引用作用域级状态——都是你在编辑器里就能看到的类型错误,而不是在高负载下才发现的堆栈跟踪。凡是编译器能够静态证明的,都会被静态校验;运行时守卫只是用来捕捉那些被 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

键的唯一性

每次注册都会返回一个被扩宽的容器类型。通过流式 API 重复注册同一个键会被拒绝:

ts
new Container()
  .registerValue('dsn', 'postgres://localhost/app')
  // TypeScript rejects this duplicate key.
  .registerValue('dsn', 'sqlite://memory')

当替换是有意为之时,测试应使用 .override()

类型中的生命周期

每个条目都同时携带值类型及其生命周期种类。类型系统会对依赖进行过滤,使单例无法直接依赖作用域级或瞬态服务。

ts
new Container()
  .registerClass('request', RequestContext, [], 'scoped')
  // Rejected: singleton cannot capture scoped request state.
  .registerClass('users', UserService, ['request'], 'singleton')

运行时严格模式仍作为针对 as 类型转换、动态键、捕获的外层容器以及依赖循环的纵深防御手段。