如何在TypeScript中为Symbol关联的接口自动生成Mock?
解决IoC容器自动生成未注册依赖Mock的方案
一、编译时存储接口与Symbol的映射信息
TypeScript接口编译后会被擦除,需要通过手动配置或编译工具留存接口与Symbol的关联信息:
手动绑定接口元数据
利用ReflectAPI给Symbol附加接口的方法、属性定义,无需额外工具,适合小型项目:export const IDependency = Symbol('IDependency'); // 手动添加接口元数据,对应IDependency的实际结构 Reflect.defineMetadata('interface:defs', { methods: ['methodA', 'methodB'], properties: ['propA'] }, IDependency); export interface IDependency { methodA(): void; methodB(): string; propA: number; }后续IoC容器解析时,可通过
Reflect.getMetadata读取元数据,生成匹配接口结构的Mock。TypeScript转换器自动生成映射
编写自定义TypeScript转换器,编译时扫描代码中@injectable关联的Symbol与同名接口,自动提取接口结构并绑定到Symbol的元数据中。这种方式无需手动维护元数据,适合依赖较多的大型项目。
二、使用通用Proxy自动生成Mock
无需接口元数据,通过Proxy拦截所有属性访问,快速生成通用Mock:
基础通用Mock实现
function createGenericMock<T>(): T { return new Proxy({} as T, { get(target, prop) { // 方法返回空mock函数(以jest为例,也可替换为自定义空函数) if (typeof prop === 'string') { return jest.fn(); } return undefined; } }); }集成到IoC容器
修改容器的解析逻辑,遇到未注册的Symbol时自动生成Mock:class TestContainer { private registrations = new Map<symbol, any>(); register<T>(token: symbol, instance: T) { this.registrations.set(token, instance); } resolve<T>(token: symbol): T { return this.registrations.get(token) ?? createGenericMock<T>(); } } // 使用示例 const container = new TestContainer(); const component = new Component(container.resolve(IDependency));增强类型适配
针对不同返回类型设置默认值,提升Mock的实用性:function createTypedMock<T>(): T { return new Proxy({} as T, { get(target, prop) { switch (prop) { case 'propA': return 0; // 匹配IDependency的propA类型 case 'methodB': return jest.fn(() => 'default-string'); default: return jest.fn(); } } }); }
三、混合方案实践
- 对需要精准模拟行为的依赖,用编译时元数据生成匹配接口的Mock
- 对仅需占位的依赖,用通用Proxy快速生成Mock
- 在IoC容器中添加配置项,允许按需指定依赖的Mock生成策略
内容的提问来源于stack exchange,提问作者Eugene Strizhok
相关产品推荐
相关产品推荐

