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

求适用于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 13:05:07