TypeScript类型运行时不生效时Angular如何识别注入类 如何实现IoC函数
问题1:Angular依赖注入的类型识别原理
Angular能在运行时识别构造参数对应的实例类型,核心依赖两项能力:
- TypeScript的装饰器元数据发射能力:只要在
tsconfig.json中开启emitDecoratorMetadata: true,TS会给所有添加了装饰器的类,在编译阶段自动生成design:paramtypes元数据,将构造函数参数对应的类构造函数,通过Reflect.defineMetadata存储到类本身。你示例中的@Component(AppComponent默认会添加)、@Injectable都属于装饰器,因此编译后MyService构造函数会作为元数据存在AppComponent类上。 - Angular DI容器的元数据读取逻辑:运行时Angular创建组件实例前,会先通过
Reflect.getMetadata('design:paramtypes', 目标类)读取构造函数的依赖列表,再从全局注入器中查找对应类型的实例(root级别的Injectable会直接返回单例,未实例化的依赖会先完成实例化再注入)。
如果注入的是接口、基础类型这类编译后会丢失的类型,Angular要求你用InjectionToken手动指定注入token,运行时直接通过token匹配实例,不需要依赖自动生成的paramtypes元数据。
问题2:实现自定义控制反转函数
前置配置
首先需要在tsconfig.json中开启两项配置,触发TS的装饰器元数据生成逻辑:
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }
如果运行环境不支持内置Reflect元数据API,可以引入reflect-metadata垫片兼容。
代码实现
- 首先实现
@Injectable装饰器,不需要额外逻辑,作用是标记类需要生成依赖元数据:
function Injectable(): ClassDecorator { return (target) => {}; }
- 维护全局DI容器存储单例,再实现
InversionOfControl函数:
type Prototype<T> = new (...args: unknown[]) => T; // 全局容器存储已实例化的单例 const diContainer = new Map<Prototype<any>, any>(); function InversionOfControl<T>(Class: Prototype<T>): T { // 已存在单例直接返回 if (diContainer.has(Class)) { return diContainer.get(Class); } // 读取构造函数的依赖元数据 const dependencies: Prototype<any>[] = Reflect.getMetadata('design:paramtypes', Class) || []; // 递归实例化所有依赖 const depsInstances = dependencies.map(dep => InversionOfControl(dep)); // 实例化当前类 const instance = new Class(...depsInstances); // 存入容器作为单例 diContainer.set(Class, instance); return instance; }
使用示例
@Injectable() class Dependency {} @Injectable() class Foo { constructor(public dependency: Dependency) {} } const foo = InversionOfControl(Foo); console.log(foo.dependency instanceof Dependency); // 输出 true
该实现默认支持类类型的依赖注入,如果需要兼容接口、基础类型的注入,可以扩展容器支持自定义注入token的映射逻辑,和Angular的InjectionToken实现逻辑一致。
内容的提问来源于stack exchange,提问作者vacih86456
相关产品推荐
相关产品推荐

