# @evyweb/ioctopus 中文文档

规范地址：https://npmcn.com/package/%40evyweb/ioctopus/

版本：1\.4\.1
npm 元数据更新时间：2026-05-08T16:39:49.781Z
中文译文更新时间：2026-11-14T02:46:05.218Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40evyweb/ioctopus
原文对照：https://npmcn.com/package/%40evyweb/ioctopus/?lang=original
项目主页：https://github.com/Evyweb/ioctopus#readme
源码仓库：https://github.com/Evyweb/ioctopus

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：51.4 kB，8 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2024-09-07T22:41:42.288Z
- 最新版本发布：2026-05-08T16:39:49.655Z
- 近 30 天下载量：54476（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：12（近 12 个月 3 个）

## 中文简介

JavaScript 和 TypeScript 用于类和函数的简单 IoC 容器.

## 安装

```sh
npm install @evyweb/ioctopus
```

也可以使用：`pnpm add @evyweb/ioctopus`、`yarn add @evyweb/ioctopus`、`bun add @evyweb/ioctopus`

## README

# TypeScript 的简单 IOC 容器

![NPM Version](https://img.shields.io/npm/v/%40evyweb%2Fioctopus.svg?style=flat) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/evyweb/ioctopus/main.yml) [![codecov](https://codecov.io/gh/Evyweb/ioctopus/graph/badge.svg?token=A3Z8UCNHDY)](https://codecov.io/gh/Evyweb/ioctopus)

![NPM Downloads](https://img.shields.io/npm/dm/%40evyweb%2Fioctopus) ![NPM Downloads](https://img.shields.io/npm/dt/%40evyweb%2Fioctopus.svg?style=flat)

![logo-ioctopus.png](https://npmcn.com/package/%40evyweb/ioctopus/assets/logo-ioctopus.png)

## 导言

用于TypeScript的IoC(倒置控制)容器.

它背后的想法是创建一个简单的容器,可以注册和解决类和函数的依赖性而无需依赖反射元数据.

它使用平版Script代码,因此可以添加到任何项目上而无需额外的依赖.

它也在"Next.js"中软件和"节点"和"边缘跑道"中工作.

## 安装

`npm i @evyweb/ioctopus`

## 如何使用

要使用章鱼,需要创建容器并绑起自属物. 为此,您需要为您想要注册的每个受抚养人创建ID.

这个ID,我们称之为"注入符",可以是字符串或符号. (请注意,你必须一致:要么总是使用字符串来约束和解决依赖关系,要么总是使用符号;你不能混合它们. )

然后可以将依赖绑定在一个值,一个函数,一个类,一个工厂,一个更高级的函数,或者一个曲折的函数上.

#### 安全类型登记(建议)

**说明:** 采用安全型登记册是改进类型安全和开发者经验的建议办法.

您可以定义一个注册类型或接口,以获取您依赖性注射密钥及其相应类型的全部类型安全性. 这就不需要在解决依赖性时进行人工铸造.

容器还验证您提供的依赖性与您的类或函数所期望的匹配。 TypeScript 将在编译时捕获错误,例如省略所需的依赖性或提供错误的依赖类型.

### 设立书记官处

定义将您的注射符映射到想要的类型的类型或界面 :

```typescript
// Using strings as injection tokens
type MyRegistry = {
    'USER_SERVICE': UserService;
    //...
}

// Or using symbols
const USER_SERVICE = Symbol('USER_SERVICE');
type MyRegistry = {
  [USER_SERVICE]: UserService;
  //...
}
```

### 创建容器

用您的注册类型创建您的容器 :

```typescript
import { createContainer } from '@evyweb/ioctopus';

// Create a type-safe container (Recommended)
const container = createContainer<MyRegistry>();
// Old way, without type safety
const container = createContainer();
```

### 注册依赖关系

#### 初级

你可以注册原始人.

```typescript
// using strings
container.bind('DEP1').toValue('dependency1');
container.bind('DEP2').toValue(42);

// using symbols
container.bind(DI.DEP1).toValue('dependency1');
container.bind(DI.DEP2).toValue(42);
```

#### 职能

-   无需依赖即可注册函数 :

```typescript
const sayHelloWorld = () => console.log('Hello World');

// using strings
container.bind('SIMPLE_FUNCTION').toFunction(sayHelloWorld);

// or using symbols
container.bind(DI.SIMPLE_FUNCTION).toFunction(sayHelloWorld);
```

#### 咖喱鱼

-   您可以使用 Currying (1级 Currying) 注册依赖函数 :

```typescript
const myFunction = (dep1: string, dep2: number) => (name: string) => console.log(`${dep1} ${dep2} ${name}`);

// using strings
container.bind('CURRIED_FUNCTION_WITH_DEPENDENCIES')
    .toCurry(myFunction, ['DEP1', 'DEP2']);

// or using symbols
container.bind(DI.CURRIED_FUNCTION_WITH_DEPENDENCIES)
    .toCurry(myFunction, [DI.DEP1, DI.DEP2]);
```

-   也可以使用依赖对象:

```typescript
interface Dependencies {
    dep1: string,
    dep2: number
}

const myFunction = (dependencies: Dependencies) => (name: string) => console.log(`${dependencies.dep1} ${dependencies.dep2} ${name}`);

// The dependencies will be listed in an object in the second parameter
// using strings
container.bind('CURRIED_FUNCTION_WITH_DEPENDENCIES')
    .toCurry(myFunction, {dep1: 'DEP1', dep2: 'DEP2'});

// or using symbols
container.bind(DI.CURRIED_FUNCTION_WITH_DEPENDENCIES)
    .toCurry(myFunction, {dep1: DI.DEP1, dep2: DI.DEP2});
```

#### 上级职能

您也可以通过使用更高排序函数来注册依赖函数 :

```typescript
const MyServiceWithDependencies = (dep1: string, dep2: number): MyServiceWithDependenciesInterface => {
    return {
        runTask: () => {
            // Do something with dep1 and dep2
        }
    };
};

// The dependencies will be listed in an array in the second parameter
// using strings
container.bind('HIGHER_ORDER_FUNCTION_WITH_DEPENDENCIES')
    .toHigherOrderFunction(MyServiceWithDependencies, ['DEP1', 'DEP2']);

// or using symbols
container.bind(DI.HIGHER_ORDER_FUNCTION_WITH_DEPENDENCIES)
    .toHigherOrderFunction(MyServiceWithDependencies, [DI.DEP1, DI.DEP2]);
```

但如果你喜欢,也可以使用依赖对象:

```typescript
interface Dependencies {
    dep1: string,
    dep2: number
}

const MyService = (dependencies: Dependencies): MyServiceInterface => {
    return {
        runTask: () => {
            // Do something with dependencies.dep1 and dependencies.dep2
        }
    };
};

// The dependencies will be listed in an object in the second parameter
// using strings
container.bind('HIGHER_ORDER_FUNCTION_WITH_DEPENDENCIES')
    .toHigherOrderFunction(MyService, {dep1: 'DEP1', dep2: 'DEP2'});

// or using symbols
container.bind(DI.HIGHER_ORDER_FUNCTION_WITH_DEPENDENCIES)
    .toHigherOrderFunction(MyService, {dep1: DI.DEP1, dep2: DI.DEP2});
```

#### 工厂

对于更复杂的案件,您可以注册工厂.

```typescript
// using strings
container.bind('MY_USE_CASE').toFactory(() => {
    // Do something before creating the instance
     
    // Then return the instance
    return MyUseCase({
        myService: container.get<MyService>('MY_SERVICE')
    });
});

// or using symbols
container.bind(DI.MY_USE_CASE).toFactory(() => {
    // Do something before creating the instance
     
    // Then return the instance
    return MyUseCase({
        myService: container.get<MyService>(DI.MY_SERVICE)
    });
});
```

#### 类

您可以注册类; 类的依赖性将被解决并注入构建器

```typescript
class MyServiceClass implements MyServiceClassInterface {
    constructor(
        private readonly dep1: string,
        private readonly dep2: number,
    ) {}

    runTask(): string {
        return `Executing with dep1: ${this.dep1} and dep2: ${this.dep2}`;
    }
}

container.bind('CLASS_WITH_DEPENDENCIES').toClass(MyServiceClass, ['DEP1', 'DEP2']);

// or using symbols
container.bind(DI.CLASS_WITH_DEPENDENCIES).toClass(MyServiceClass, [DI.DEP1, DI.DEP2]);
```

但如果你喜欢,也可以使用依赖对象:

```typescript

interface Dependencies {
    dep1: string,
    dep2: number
}

class MyServiceClass implements MyServiceClassInterface {
    constructor(private readonly dependencies: Dependencies) {}

    runTask(): string {
        return `Executing with dep1: ${this.dependencies.dep1} and dep2: ${this.dependencies.dep2}`;
    }
}

container.bind('CLASS_WITH_DEPENDENCIES').toClass(MyServiceClass, {dep1: 'DEP1', dep2: 'DEP2'});

// or using symbols
container.bind(DI.CLASS_WITH_DEPENDENCIES).toClass(MyServiceClass, {dep1: DI.DEP1, dep2: DI.DEP2});
```

-   无需依赖即可注册课程:

```typescript
class MyServiceClassWithoutDependencies implements MyServiceClassInterface {
    runTask(): string {
        return `Executing without dependencies`;
    }
}

container.bind('CLASS_WITHOUT_DEPENDENCIES').toClass(MyServiceClassWithoutDependencies);

// or using symbols
container.bind(DI.CLASS_WITHOUT_DEPENDENCIES).toClass(MyServiceClassWithoutDependencies);
```

### 使用登记册解决依赖关系(建议)

您现在可以用容器的获取方法解决依赖性 .

```typescript
import { DI } from './di';

// Primitive
// using strings
const dep1 = container.get('DEP1'); // type will be the one you registered in your registry (string here)

// or using symbols
const dep1 = container.get(DI.DEP1); // type will be the one you registered in your registry (string here)
```

### 在没有登记册的情况下解决依赖性

```typescript
import { DI } from './di';

// Primitive
// using strings
const dep1 = container.get<string>('DEP1'); // By default, the type will be 'unknown', so you need to cast it manually

// or using symbols
const dep1 = container.get<string>(DI.DEP1); // By default, the type will be 'unknown', so you need to cast it manually
```

### 模块

您也可以使用模块来组织您的依赖。 模块支持与集装箱相同的类型安全登记办法.

#### 正在装入模块

然后可将模块装入容器. 默认情况下,在创建容器时,会使用罩下的一个默认模块.

**有书记官处(建议):**

```typescript
interface AppRegistry {
    'DEP1': string;
    'DEP2': number;
    'MY_SERVICE': MyServiceInterface;
}

const module1 = createModule<AppRegistry>();
module1.bind('DEP1').toValue('dependency1');

const module2 = createModule<AppRegistry>();
module2.bind('DEP2').toValue(42);

const module3 = createModule<AppRegistry>();
module3.bind('MY_SERVICE').toHigherOrderFunction(MyService, {dep1: 'DEP1', dep2: 'DEP2'});

const container = createContainer<AppRegistry>();
container.load('module1', module1);
container.load('module2', module2);
container.load('module3', module3);

const myService = container.get('MY_SERVICE'); // Type: MyServiceInterface
```

**未登记旧做法:**

```typescript
const module1 = createModule();
module1.bind(DI.DEP1).toValue('dependency1');

const module2 = createModule();
module2.bind(DI.DEP2).toValue(42);

const module3 = createModule();
module3.bind(DI.MY_SERVICE).toHigherOrderFunction(MyService, {dep1: DI.DEP1, dep2: DI.DEP2});

const container = createContainer();
container.load(Symbol('module1'), module1);
container.load(Symbol('module2'), module2);
container.load(Symbol('module3'), module3);

const myService = container.get<MyServiceInterface>(DI.MY_SERVICE);
```

依赖性不需要在使用它们的模块中注册. 注意作为密钥使用的模块名可以是符号或字符串.

#### 模块覆盖

您也可以覆盖模块的依赖性 。 模块的依赖性将被上一个已上载模块的依赖性所压倒.

```typescript
const module1 = createModule<AppRegistry>();
module1.bind(DI.DEP1).toValue('OLD dependency1');
module1.bind(DI.MY_SERVICE).toFunction(sayHelloWorld);

const module2 = createModule<AppRegistry>();
module2.bind(DI.DEP1).toValue('NEW dependency1');

const module3 = createModule<AppRegistry>();
module3.bind(DI.MY_SERVICE).toHigherOrderFunction(MyService, {dep1: DI.DEP1, dep2: DI.DEP2});

const container = createContainer<AppRegistry>();
container.bind(DI.DEP2).toValue(42); // Default module
container.load(Symbol('module1'), module1);
container.load(Symbol('module2'), module2);
container.load(Symbol('module3'), module3);

// The dependency DI.MY_SERVICE will be resolved with the higher-order function and dep1 will be 'NEW dependency1'
const myService = container.get(DI.MY_SERVICE);
```

#### 卸载模块

也可以从容器中卸下一个模块. 模块的依赖性将从容器中去除. 已缓存实例将被删除,以保持一致性并避免可能出错.

```typescript
const module1 = createModule<AppRegistry>();
module1.bind(DI.DEP1).toValue('dependency1');

const container = createContainer<AppRegistry>();
container.load(Symbol('module1'), module1);

container.unload(Symbol('module1'));

// Will throw an error as the dependency is not registered anymore
const myService = container.get(DI.DEP1); 
```

### 使用范围

#### 单通范围( 默认)

在单顿范围内,容器每次解决依赖性时都会返回同样的情况.

```typescript
container.bind(DI.MY_SERVICE).toClass(MyServiceClass, [DI.DEP1, DI.DEP2]);
// or
container.bind(DI.MY_SERVICE).toClass(MyServiceClass, [DI.DEP1, DI.DEP2], 'singleton');

const instance1 = container.get(DI.MY_SERVICE);
const instance2 = container.get(DI.MY_SERVICE);

console.log(instance1 === instance2); // true
```

#### 临时范围

在瞬态范围,容器每次解决依赖性时都会返回一个新的实例.

```typescript
container.bind(DI.MY_SERVICE).toClass(MyServiceClass, [DI.DEP1, DI.DEP2], 'transient');

const instance1 = container.get(DI.MY_SERVICE);
const instance2 = container.get(DI.MY_SERVICE);

console.log(instance1 === instance2); // false
```

#### 范围

在范围范围内,容器在一个范围内返回同一情况。 不同的范围会有不同的情况.

要使用范围范围,您需要使用 runInScope 创建范围.

```typescript
container.bind(DI.MY_SERVICE).toClass(MyServiceClass, [DI.DEP1, DI.DEP2], 'scoped');
const instance1 = undefined;
const instance2 = undefined;

container.runInScope(() => {
    instance1 = container.get(DI.MY_SERVICE);
    instance2 = container.get(DI.MY_SERVICE);

    console.log(instance1 === instance2); // true
});

container.runInScope(() => {
    const instance3 = container.get(DI.MY_SERVICE);

    console.log(instance3 === instance1); // false
});
```

注:如果试图解决范围外的依赖性,则会出现出错.

### 循环依赖关系

章鱼容器可以探测到循环依赖性. 如果检测到循环依赖, 则会出现出错 .

```typescript
const container = createContainer<AppRegistry>();

const A_TOKEN = Symbol('A');
const B_TOKEN = Symbol('B');

class A {
    constructor(public b: B) {}
}

class B {
    constructor(public a: A) {}
}

container.bind(A_TOKEN).toClass(A, [B_TOKEN]);
container.bind(B_TOKEN).toClass(B, [A_TOKEN]);

container.get(A_TOKEN); // Will throw: "Circular dependency detected: Symbol(A) -> Symbol(B) -> Symbol(A)"
```

这样可以避免无限循环和堆叠溢出的错误.

## 常见问题

### @evyweb/ioctopus 是什么？

JavaScript 和 TypeScript 用于类和函数的简单 IoC 容器\.

### 如何安装 @evyweb/ioctopus？

在项目目录运行 npm install @evyweb/ioctopus，或使用 pnpm add / yarn add / bun add。

### @evyweb/ioctopus 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @evyweb/ioctopus 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @evyweb/ioctopus 可以商用吗？

@evyweb/ioctopus 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @evyweb/ioctopus 的下载量有多少？

根据 npm 官方下载统计，@evyweb/ioctopus 在 2026/10/14 – 2026/11/12 这 30 天内被下载 54,476 次，日均约 1,816 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @evyweb/ioctopus 还在维护吗？

最新版本 1\.4\.1 发布于 2026/05/09（6 个月前）；至今共发布 12 个版本，其中过去 12 个月发布了 3 个；首次发布于 2024/09/08。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

