求适用于TypeScript函数式编程的依赖注入容器(替代OOP类库)
适用于TypeScript函数式编程的依赖注入容器推荐
注意:TypeScript的接口仅存在于编译阶段,无法作为运行时的绑定key,所以实际使用时需要用
Symbol或字符串作为唯一标识来关联接口与实现。
1. Awilix
Awilix 同时支持面向对象与函数式风格的依赖注入,对TypeScript有完善的类型支持,能轻松实现你需要的「绑定函数式实现、测试时替换假依赖」的需求。
适配你的示例实现:
容器初始化
// magic-container.ts import { createContainer, asValue } from 'awilix'; const magicContainer = createContainer(); export default magicContainer;
Express路由中绑定真实实现
import { Router } from 'express'; import { UserRepositoryInterface, UserRepository } from '...'; import magicContainer from './magic-container'; // 用Symbol作为接口的运行时标识 export const USER_REPO = Symbol('UserRepositoryInterface'); const router = Router(); // 注册真实的函数式实现 magicContainer.register({ [USER_REPO]: asValue(UserRepository) }); router.post('/my-endpoint', async (req, res) => { // 从容器获取依赖 const userRepo = magicContainer.resolve<UserRepositoryInterface>(USER_REPO); const users = await userRepo.myFunction(); res.status(200).json(users); });
端到端测试中替换为假实现
import supertest from 'supertest'; import app from '../app'; import { HttpStatus } from '...'; import magicContainer from './magic-container'; import { USER_REPO, FakeUserRepository } from '..'; describe('My test describe', () => { test('My test', async () => { // 替换为假实现 magicContainer.register({ [USER_REPO]: asValue(FakeUserRepository) }); await supertest(app) .post('/my-endpoint') .send({ payload: {} }) .expect(HttpStatus.OK); }); });
2. fn-di
fn-di 是专门为函数式编程设计的轻量DI容器,完全适配TypeScript,核心逻辑围绕「函数与值的注入」设计,没有面向对象的冗余特性。
适配你的示例实现:
容器初始化
// magic-container.ts import { createContainer } from 'fn-di'; const magicContainer = createContainer(); export default magicContainer;
路由中绑定与使用依赖
import { Router } from 'express'; import { UserRepositoryInterface, UserRepository } from '...'; import magicContainer from './magic-container'; const USER_REPO = Symbol('UserRepositoryInterface'); // 绑定标识与函数式实现 magicContainer.bind(USER_REPO, UserRepository); const router = Router(); router.post('/my-endpoint', async (req, res) => { const userRepo = magicContainer.get<UserRepositoryInterface>(USER_REPO); const users = await userRepo.myFunction(); res.status(200).json(users); });
测试中替换依赖
import supertest from 'supertest'; import app from '../app'; import { HttpStatus } from '...'; import magicContainer from './magic-container'; import { USER_REPO, FakeUserRepository } from '..'; describe('My test describe', () => { test('My test', async () => { // 重新绑定为假实现 magicContainer.rebind(USER_REPO, FakeUserRepository); await supertest(app) .post('/my-endpoint') .send({ payload: {} }) .expect(HttpStatus.OK); }); });
3. 手动实现轻量容器
如果你的需求场景简单,手动实现一个极简DI容器反而更灵活,完全贴合你的使用习惯:
// magic-container.ts type ContainerBindings = Map<symbol | string, unknown>; class MagicContainer { private bindings: ContainerBindings = new Map(); bind<T>(key: symbol | string, value: T): void { this.bindings.set(key, value); } resolve<T>(key: symbol | string): T { const value = this.bindings.get(key); if (!value) throw new Error(`未找到绑定key: ${String(key)}`); return value as T; } rebind<T>(key: symbol | string, value: T): void { this.bind(key, value); } } const magicContainer = new MagicContainer(); export default magicContainer;
使用示例
定义接口与标识
// user-repo.ts export const USER_REPO = Symbol('UserRepositoryInterface'); export interface UserRepositoryInterface { myFunction: () => Promise<User[]>; } export const UserRepository: UserRepositoryInterface = { myFunction: async () => { // 真实业务逻辑 } }; export const FakeUserRepository: UserRepositoryInterface = { myFunction: async () => { // 测试用假逻辑 } };
路由中使用
import { Router } from 'express'; import { USER_REPO, UserRepository } from '...'; import magicContainer from './magic-container'; const router = Router(); magicContainer.bind(USER_REPO, UserRepository); router.post('/my-endpoint', async (req, res) => { const userRepo = magicContainer.resolve<UserRepositoryInterface>(USER_REPO); const users = await userRepo.myFunction(); res.status(200).json(users); });
测试中替换
import supertest from 'supertest'; import app from '../app'; import { HttpStatus } from '...'; import magicContainer from './magic-container'; import { USER_REPO, FakeUserRepository } from '..'; describe('My test describe', () => { test('My test', async () => { magicContainer.rebind(USER_REPO, FakeUserRepository); await supertest(app) .post('/my-endpoint') .send({ payload: {} }) .expect(HttpStatus.OK); }); });
内容的提问来源于stack exchange,提问作者user3589975
相关产品推荐
相关产品推荐

