如何为带额外依赖的外部TypeScript库类注入环境并实现Angular单例
问题描述
我有一个名为my-lib的TypeScript库,包含两个类:
my-lib/token-manager.ts
export class TokenManager { // ... log() { console.log('From my TokenManager class') } }
my-lib/api-connection.ts
import { TokenManager } from "./token-manager" export class ApiConnection { constructor( private tm: TokenManager, environment: any ) { // ... console.log(environment) } doSomething() { this.tm.log() } }
我在Angular项目中已将该库作为依赖安装:
project1/package.json
{ ... "dependencies": { ... "my-lib": "github:myname/my-lib", ... } ... }
项目中的SigninComponent需要使用这个库:
project1/src/app/signin.component.ts
import { Component } from '@angular/core'; import { ApiConnection } from 'my-lib/api-connection'; @Component({ selector: 'app-signin', templateUrl: './signin.component.html', styleUrls: ['./signin.component.scss'] }) export class SigninComponent { constructor(private apiConnection: ApiConnection) { } onSubmit() { // ... this.apiConnection.doSomething() // ... } }
运行后浏览器控制台报错:
ERROR Error: Uncaught (in promise): Error: ASSERTION ERROR: token must be defined [Expected=> null != undefined <=Actual] throwError@http://localhost:4200/vendor.js:22105:9 assertDefined@http://localhost:4200/vendor.js:22101:15 bloomHashBitOrFactory@http://localhost:4200/vendor.js:26573:16 lookupTokenUsingNodeInjector@http://localhost:4200/vendor.js:26357:42 getOrCreateInjectable@http://localhost:4200/vendor.js:26338:47 ɵɵdirectiveInject@http://localhost:4200/vendor.js:33315:38 SigninComponent_Factory@http://localhost:4200/main.js:491:82 ...
ApiConnection类可以自行创建TokenManager,但需要外部提供environment值。我尝试在app.module.ts中配置,但不知道如何正确提供值,也无法实现单例:
project1 app.module.ts
... import { environment } from 'src/environments/environment'; ... @NgModule({ ... providers: [ { provide: ApiConnection, useValue: environment } ], ... })
核心问题:我不能让该库依赖@angular/core,也无法在库中使用Angular的@Inject装饰器。请问是否可以为外部库类的构造函数提供环境值,并让该类实例成为单例,供Angular项目中所有组件和服务复用?
解决方案
完全可以实现,无需修改库代码,只需在Angular项目中通过工厂提供者配置依赖注入即可:
步骤1:在AppModule中配置ApiConnection的工厂提供者
修改app.module.ts,使用useFactory创建ApiConnection实例,传入所需的environment参数,同时适配库的实例化逻辑:
... import { environment } from 'src/environments/environment'; import { ApiConnection } from 'my-lib/api-connection'; import { TokenManager } from 'my-lib/token-manager'; ... @NgModule({ ... providers: [ // 声明TokenManager为单例提供者(若库允许共享该实例) TokenManager, // 配置ApiConnection的工厂提供者 { provide: ApiConnection, useFactory: (tokenManager: TokenManager) => { // 传入TokenManager实例和项目环境变量,创建ApiConnection return new ApiConnection(tokenManager, environment); }, // 声明工厂函数的依赖:Angular会自动注入TokenManager实例 deps: [TokenManager] } ], ... }) export class AppModule { }
步骤2:验证单例与功能
配置完成后,Angular会:
- 创建
TokenManager的单例实例 - 通过工厂函数生成
ApiConnection的单例实例,传入TokenManager单例和项目environment - 所有组件/服务中注入的
ApiConnection都是同一个实例
此时SigninComponent的代码无需修改,运行后可正常调用apiConnection.doSomething(),environment也会被正确传入ApiConnection的构造函数。
额外适配:若ApiConnection需自行创建TokenManager
如果库要求ApiConnection自行实例化TokenManager(不需要共享单例),可修改工厂函数:
useFactory: () => { const tm = new TokenManager(); return new ApiConnection(tm, environment); }, deps: [] // 无需依赖注入TokenManager
这种方式完全遵循库的设计逻辑,同时满足Angular的依赖注入规则,实现单例复用。
内容的提问来源于stack exchange,提问作者smk
相关产品推荐
相关产品推荐

