Skip to content

API 概览

本页总结了公开的核心 API。准确的泛型定义请参阅软件包 README 和 TypeScript 声明文件。

Container

ts
import {
  Container,
  type ContainerOptions,
  type DependenciesMap,
  type Lazy,
  type AsyncLazy,
  type LazySpec,
  type AsyncLazySpec,
  type AsyncSpec,
  type Module,
  type Lifetime,
  type ScopeInputMap,
  type Spec,
  type SpecMap,
  type WithRequirements
} from '@inferdi/inferdi'
ts
class Container<T extends DependenciesMap = Record<never, never>> {
  constructor(options?: ContainerOptions)

  declareScopeInputs<Inputs>()
  registerClass(key, Ctor, deps, lifetime?, lazyKey?)
  registerFactory(key, factory, lifetime?)
  registerFactory(key, factory, lifetime, lazyKey)
  registerFactory(key, factory, deps, lifetime?)
  registerFactory(key, factory, deps, lifetime, lazyKey)
  registerAsyncFactory(key, factory, deps, lifetime?, lazyKey?)
  registerValue(key, value)
  override(key, value)
  use(fn)

  createScope(inputs?)
  get(syncReadyKey)
  getAsync(readyKey): Promise
  has(key): key is keyof T

  get disposed(): boolean
  dispose(): Promise<void>
  [Symbol.dispose](): void
  [Symbol.asyncDispose](): Promise<void>
}

容器选项

构造函数接受一个可选设置:

选项类型默认值用途
fastbooleanfalse在经过检查的可变契约与未经运行时检查的固定图契约之间选择
ts
const checked = new Container()
const explicitChecked = new Container({ fast: false })
const fast = new Container({ fast: true })

默认形式和显式 false 会保留运行时循环与生命周期检查,禁止从根容器解析 scoped 服务,并使用精确的可变父链。开发、测试、热重载以及启动后仍可能变更的 依赖图都应使用此契约。

字面量 {fast: true} 保留所有 TypeScript 检查,但关闭运行时循环与生命周期记录, 包括根容器的 scoped 检查。它把容器树视为固定结构,让作用域直接访问注册表所有者, 并将委托解析的 singleton 镜像到本地作用域缓存中。子作用域继承根容器的配置。

在 fast 容器树中,必须在第一次解析或调用 createScope() 前完成所有 register*.use().override() 调用;激活后保持容器树不变,并先释放子容器,再释放祖先 容器。只有字面量 true 会启用此契约;其他运行时值会回退到经过检查的契约。 性能说明了此选择影响的操作,以及何时值得采用这一取舍。

注册方法

方法回调输入图中类型解析方法
registerClassdeps 对应的构造函数参数Spec 或传播后的 AsyncSpecgetgetAsync
registerFactory(key, factory, ...)按生命周期过滤的容器Spec<ReturnType>get
registerFactory(key, factory, deps, ...)仅含 deps 的 resolver带输入要求的 Specget
registerAsyncFactory位置参数;声明式 async 依赖会先被等待AsyncSpec<Awaited<ReturnType>>getAsync
registerValue外部所有的 singleton Specget

registerClassregisterFactoryregisterAsyncFactory 接受 singletonscopedtransient 三种生命周期。registerFactory 创建伴随项时必须显式传入生命周期,包括 'singleton'registerValue 始终为单例,且由外部拥有。

registerClassregisterFactory 中传入 lazyKey 会添加一个受管理的 LazySpec。如果类从依赖传播得到 async 状态,则改为添加 AsyncLazySpec。返回 Promise 的 registerFactory 仍是同步的 Spec<Promise<T>>,其伴随项仍是 Lazy<Promise<T>>;若依赖图应保存最终服务类型,请使用 registerAsyncFactory

registerAsyncFactory 接受相同的生命周期和可选的第五个 lazyKey。主注册用 AsyncSpec 保存最终服务类型,伴随项类型为 AsyncLazySpec<Awaited<ReturnType>, L>。依赖该目标键的类会继承 async 状态,而包装器的使用者仍保持同步。目标使用 getAsync(),包装器使用 get()

registerAsyncFactory 以及依赖元组可能选中异步键的 registerClass 调用都要求只读元组。InferDI 在注册时只分类一次异步位置,并保留该元组引用。内联字面量会推断为只读;仅同步的 registerClass 调用仍支持可变元组。

带依赖的 registerFactoryregisterAsyncFactory 使用相同的参数顺序,但回调契约不同。前者接收仅限于 deps 的 resolver,后者按位置接收依赖值:

ts
registerFactory(key, resolverFactory, deps, lifetime, lazyKey)
registerAsyncFactory(key, valueFactory, deps, lifetime, lazyKey)

override 替换现有的非 scope-input 注册,use 应用模块构建器。override 的时机检查只查看当前容器的缓存。它能发现本地缓存的 singleton/scoped 值、registerValue 和重复覆盖,但从不记录 transient 解析。在 checked 模式下,它也不会记录通过子容器解析但由祖先容器拥有的 singleton;fast 子容器会把委托的 singleton 镜像到本地缓存,因此检查能够发现这些值。请在解析依赖图之前应用覆盖。

作用域输入与解析

declareScopeInputs<Inputs>() 添加仅存在于类型中的 scoped 项。createScope(inputs) 提供缺少值的任意子集,并返回就绪键集合与必填属性对应的容器。输入值仍由应用所有。

API接受的键
get()不含 AsyncSpec 的就绪键
getAsync()所有就绪的同步键和声明式异步键
has()任意 string 或 symbol;只证明注册存在

has() 不能证明键已就绪或属于同步键。声明的 scope input 只存在于类型中,并不是注册;即使 createScope(inputs) 已提供相应值,has() 对这些键仍返回 false。类型状态细化见作用域输入,Promise 行为见异步依赖

命名空间类型

ts
namespace Container {
  type ReadyKeys<C>
  type SyncReadyKeys<C>
  type Resolve<C>
  type ResolveUnwrapped<C>
  type UnwrappedValue<C, K>
  type Providers<C>
}
类型用途
Container.ReadyKeys<C>提取已提供作用域输入的键;泛型解析器可将这些键传给 getAsync
Container.SyncReadyKeys<C>提取可由泛型解析器传给 get 的已就绪非异步键。
Container.Resolve<C>从已构建的容器中提取一个扁平的 { key: Value } 映射。
Container.ResolveUnwrapped<C>类似 Resolve,但以 distributive 方式解包受管理的 LazySpecAsyncLazySpec;普通包装器服务保持不变。
Container.UnwrappedValue<C, K>查询单个已解包的服务类型。
Container.Providers<C>为测试创建一组 provider thunk 的映射;声明的 scope input 会被排除。

v6 的泛型 resolver 必须保留可接受的键集合。get() 使用 Container.SyncReadyKeys<C>getAsync() 使用 Container.ReadyKeys<C>,不要使用不受约束的 keyof T

公开类型

ts
type Lazy<T> = { readonly get: () => T }
type AsyncLazy<T> = { readonly get: () => Promise<T> }
type Lifetime = 'singleton' | 'scoped' | 'transient'
type DependenciesMap = Record<
  string | symbol,
  Spec<unknown, Lifetime>
>

interface ContainerOptions {
  readonly fast?: boolean
}

interface Spec<V, L extends Lifetime = 'singleton'> {
  readonly type: V
  readonly lifetime: L
}

interface AsyncSpec<V, L extends Lifetime = 'singleton'>
  extends Spec<V, L> {
  readonly async: true
}

interface LazySpec<V, TargetLifetime extends Lifetime>
  extends Spec<Lazy<V>, 'transient'> {
  readonly lazyOf: TargetLifetime
}

interface AsyncLazySpec<V, TargetLifetime extends Lifetime>
  extends Spec<AsyncLazy<V>, 'transient'> {
  readonly lazyOf: TargetLifetime
}

type SpecMap<M, L extends Lifetime = 'singleton'> = {
  [P in keyof M]: Spec<M[P], L>
}

type Module<TRequirements extends DependenciesMap, TProvides extends DependenciesMap> =
  (c: Container<TRequirements>) => Container<TRequirements & TProvides>

LazySpecAsyncLazySpec 带有私有的 type-only 判别字段。显式 ContainerModule 形状应使用这些具名类型。该字段没有运行时值,也不导出。

SpecAsyncSpecLazySpecAsyncLazySpec 描述类型级依赖图中的条目;它们的字段不会添加到解析得到的服务值上。

ScopeInputMap<M> 把必填且有限的 string/symbol 属性映射为 scoped input 项。它会拒绝可选键、数字键、__proto__、宽泛索引签名以及键集合不同的联合类型。WithRequirements<S, K> 把所需输入键附加到依赖图条目上,适合用于具名模块输出。准确的条件类型定义以发布的 TypeScript 声明为准。

适配器 API 形态

HTTP 适配器

Fastify、Hono、Koa、Express 和 Elysia 导出:

  • 集成函数,例如 inferdiFastify
  • skipInferdiDispose
  • MaybePromise
  • 结构化的 InferdiScopeInferdiRootInferdiScopeOf 辅助类型
  • 框架专属的选项与上下文辅助类型

React 适配器

React 导出 inferdiReact,以及 binding、外部 Provider、托管 ScopeProvider、服务 Hook、依赖图提取、稳定同步/异步键和作用域生命周期选项的类型。它不导出 skipInferdiDispose,因为组件作用域跟随 React 的提交与 effect 清理,而不是 HTTP 请求生命周期。

准确名称和生命周期细节请参阅各适配器页面。