You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript DI框架依赖参数类型推断问题求助

实现TypeScript DI框架的依赖参数类型自动推断

要解决根据deps键自动推断factory依赖参数类型的问题,我们需要通过TypeScript的条件类型、映射类型和递归泛型,将deps数组的每个键与对应的服务/模块服务类型绑定,并约束factory的参数元组与deps的顺序、类型完全匹配。

核心类型工具解析

  1. ExtractModuleServices:提取模块factory返回的服务对象类型

    type ExtractModuleServices<T extends () => Promise<(...args: any[]) => ModuleReturn>> = 
      NonNullable<ReturnType<Awaited<ReturnType<T>>>>['services'];
    
  2. ResolveDep:解析单个依赖键对应的实际类型,区分全局服务(如cache)和模块服务(如data.createResource)

    type ResolveDep<
      Modules extends Record<string, { factory: () => Promise<(...args: any[]) => ModuleReturn> }>,
      Services extends Record<string, { factory: () => Promise<(...args: any[]) => any> }>,
      Dep extends string
    > = 
      Dep extends `${infer ModName}.${infer ServName}` 
        ? ModName extends keyof Modules 
          ? ServName extends keyof ExtractModuleServices<Modules[ModName]['factory']>
            ? ExtractModuleServices<Modules[ModName]['factory']>[ServName]
            : never
          : never
        : Dep extends keyof Services
          ? ReturnType<Awaited<ReturnType<Services[Dep]['factory']>>>
          : never;
    
  3. DepsToParams:将deps数组转换为对应的参数类型元组,保证顺序一致

    type DepsToParams<
      Modules extends Record<string, any>,
      Services extends Record<string, any>,
      Deps extends string[]
    > = { [K in keyof Deps]: ResolveDep<Modules, Services, Deps[K]> };
    
  4. AllowedDeps:约束deps的有效值,只能是已定义的全局服务或模块服务键

    type AllowedDeps<
      Modules extends Record<string, { factory: () => Promise<(...args: any[]) => ModuleReturn> }>,
      Services extends Record<string, any>
    > = 
      | (keyof Services & string)
      | { [K in keyof Modules & string]: `${K}.${ExtractModuleServiceKeys<Modules[K]['factory']>}` }[keyof Modules & string];
    

完整实现代码

type ModuleReturn = {
  services?: Record<string, any>;
};

// 提取模块factory返回的服务类型
type ExtractModuleServices<T extends () => Promise<(...args: any[]) => ModuleReturn>> = 
  NonNullable<ReturnType<Awaited<ReturnType<T>>>>['services'];

// 提取模块的服务键名
type ExtractModuleServiceKeys<T extends () => Promise<(...args: any[]) => ModuleReturn>> = 
  keyof ExtractModuleServices<T> & string;

// 解析单个依赖对应的类型
type ResolveDep<
  Modules extends Record<string, { factory: () => Promise<(...args: any[]) => ModuleReturn> }>,
  Services extends Record<string, { factory: () => Promise<(...args: any[]) => any> }>,
  Dep extends string
> = 
  // 处理模块服务,如 "data.createResource"
  Dep extends `${infer ModName}.${infer ServName}` 
    ? ModName extends keyof Modules 
      ? ServName extends keyof ExtractModuleServices<Modules[ModName]['factory']>
        ? ExtractModuleServices<Modules[ModName]['factory']>[ServName]
        : never
      : never
    // 处理全局服务,如 "cache"
    : Dep extends keyof Services
      ? ReturnType<Awaited<ReturnType<Services[Dep]['factory']>>>
      : never;

// 将deps数组转换为对应的参数类型元组
type DepsToParams<
  Modules extends Record<string, any>,
  Services extends Record<string, any>,
  Deps extends string[]
> = { [K in keyof Deps]: ResolveDep<Modules, Services, Deps[K]> };

// 单个模块的配置类型,关联deps和factory参数
type ModuleDefinition<
  Modules extends Record<string, any>,
  Services extends Record<string, any>,
  Deps extends string[] = []
> = {
  deps?: Deps;
  enabled?: (state: any) => boolean;
  factory: () => Promise<(...args: DepsToParams<Modules, Services, Deps>) => ModuleReturn>;
};

// 单个服务的配置类型,关联deps和factory参数
type ServiceDefinition<
  Modules extends Record<string, any>,
  Services extends Record<string, any>,
  Deps extends string[] = []
> = {
  deps?: Deps;
  factory: () => Promise<(...args: DepsToParams<Modules, Services, Deps>) => any>;
};

// 约束deps的有效值:只能是全局服务或模块服务
type AllowedDeps<
  Modules extends Record<string, { factory: () => Promise<(...args: any[]) => ModuleReturn> }>,
  Services extends Record<string, any>
> = 
  | (keyof Services & string)
  | { [K in keyof Modules & string]: `${K}.${ExtractModuleServiceKeys<Modules[K]['factory']>}` }[keyof Modules & string];

// 最终的App配置类型,确保每个模块/服务的deps都是有效值,且factory参数匹配deps类型
type AppConfig<
  Modules extends Record<string, ModuleDefinition<Modules, Services>>,
  Services extends Record<string, ServiceDefinition<Modules, Services>>
> = {
  modules: {
    [K in keyof Modules]: Omit<Modules[K], 'deps'> & {
      deps?: AllowedDeps<Modules, Services>[];
    };
  };
  services: {
    [K in keyof Services]: Omit<Services[K], 'deps'> & {
      deps?: Exclude<AllowedDeps<Modules, Services>, K>[];
    };
  };
};

// 实现createApp函数
export async function createApp<
  Modules extends Record<string, ModuleDefinition<Modules, Services>>,
  Services extends Record<string, ServiceDefinition<Modules, Services>>
>(config: AppConfig<Modules, Services>) {}

测试示例

const app = await createApp({
  modules: {
    data: {
      factory: () => Promise.resolve(() => ({
        services: {
          createResource: () => {}
        }
      }))
    },
    users: {
      deps: ['data.createResource'],
      factory: () => Promise.resolve((createResource: () => void) => ({})), // ✅ 无报错:参数类型匹配deps
    },
    users2: {
      deps: ['data.createResource'],
      factory: () => Promise.resolve(() => ({})), // ❌ 报错:缺少对应依赖参数
    },
    users3: {
      deps: ['cache'],
      factory: () => Promise.resolve(() => ({})), // ❌ 报错:缺少对应依赖参数
    },
    users4: {
      deps: ['cache', 'data.createResource'],
      factory: () => Promise.resolve(() => ({})), // ❌ 报错:缺少两个依赖参数
    },
    users5: {
      deps: ['cache', 'data.createResource'],
      factory: () => Promise.resolve((cache: {}, createResource: () => void) => ({})), // ✅ 无报错:参数顺序、类型匹配
    },
  },
  services: {
    cache: { factory: () => Promise.resolve(() => ({})) },
  },
});

效果说明

  • 当factory的参数元组与deps数组对应的类型、顺序完全一致时,TypeScript不会报错;
  • 当参数缺失、顺序错误或类型不匹配时,TypeScript会立即抛出类型错误,符合预期的校验逻辑。

内容的提问来源于stack exchange,提问作者Andy Jessop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 22:09:22