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

如何为带额外依赖的外部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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:35:56