Skip to content

Commit 7720434

Browse files
author
Umed Khudoiberdiev
committed
added support for function DI
1 parent 615530e commit 7720434

File tree

12 files changed

+257
-43
lines changed

12 files changed

+257
-43
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.8.0
4+
5+
* added new type of dependency injection - function DI
6+
37
## 0.7.2
48

59
* fixed bug with inherited services

README.md

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ coffeeMaker.make();
7777

7878
With TypeDI you can use a named services. Example:
7979

80-
```typescript
80+
```javascript
8181
var Container = require("typedi").Container;
8282

8383
class BeanFactory implements Factory {
@@ -127,7 +127,7 @@ coffeeMaker.make();
127127
This feature especially useful if you want to store (and inject later on) some settings or configuration options.
128128
For example:
129129

130-
```typescript
130+
```javascript
131131
var Container = require("typedi").Container;
132132

133133
// somewhere in your global app parameters
@@ -144,7 +144,7 @@ class UserRepository {
144144

145145
When you write tests you can easily provide your own "fake" dependencies to classes you are testing using `set` method:
146146

147-
```typescript
147+
```javascript
148148
Container.set(CoffeeMaker, new FakeCoffeeMaker());
149149

150150
// or for named services
@@ -156,6 +156,47 @@ Container.set([
156156
]);
157157
```
158158

159+
TypeDI also supports a function dependency injection. Here is how it looks like:
160+
161+
162+
```javascript
163+
var Service = require("typedi").Service;
164+
var Container = require("typedi").Container;
165+
166+
var PostRepository = Service(() => ({
167+
getName() {
168+
return "hello from post repository";
169+
}
170+
}));
171+
172+
var PostManager = Service(() => ({
173+
getId() {
174+
return "some post id";
175+
}
176+
}));
177+
178+
class PostQueryBuilder {
179+
build() {
180+
return "SUPER * QUERY";
181+
}
182+
}
183+
184+
var PostController = Service([
185+
PostManager,
186+
PostRepository,
187+
PostQueryBuilder
188+
], (manager, repository, queryBuilder) => {
189+
return {
190+
id: manager.getId(),
191+
name: repository.getName(),
192+
query: queryBuilder.build()
193+
};
194+
});
195+
196+
var postController = Container.get(PostController);
197+
console.log(postController);
198+
```
199+
159200
## Usage with TypeScript
160201

161202
1. Install module:
@@ -715,6 +756,44 @@ export class QuestionUtils {
715756

716757
And this global service will be the same instance across all containers.
717758

759+
TypeDI also supports a function dependency injection. Here is how it looks like:
760+
761+
762+
```javascript
763+
export const PostRepository = Service(() => ({
764+
getName() {
765+
return "hello from post repository";
766+
}
767+
}));
768+
769+
export const PostManager = Service(() => ({
770+
getId() {
771+
return "some post id";
772+
}
773+
}));
774+
775+
export class PostQueryBuilder {
776+
build() {
777+
return "SUPER * QUERY";
778+
}
779+
}
780+
781+
export const PostController = Service([
782+
PostManager,
783+
PostRepository,
784+
PostQueryBuilder
785+
], (manager, repository, queryBuilder) => {
786+
return {
787+
id: manager.getId(),
788+
name: repository.getName(),
789+
query: queryBuilder.build()
790+
};
791+
});
792+
793+
const postController = Container.get(PostController);
794+
console.log(postController);
795+
```
796+
718797
### Remove registered services or reset container state
719798

720799
If you need to remove registered service from container simply use `Container.remove(...)` method.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {Service} from "../../src/decorators/Service";
2+
import {PostManager} from "./PostManager";
3+
import {PostQueryBuilder} from "./PostQueryBuilder";
4+
import {PostRepository} from "./PostRepository";
5+
6+
export const PostController = Service([
7+
PostManager,
8+
PostRepository,
9+
PostQueryBuilder
10+
], (manager, repository, queryBuilder) => {
11+
return {
12+
id: manager.getId(),
13+
name: repository.getName(),
14+
query: queryBuilder.build()
15+
};
16+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {Service} from "../../src/decorators/Service";
2+
3+
export const PostManager = Service(() => ({
4+
getId() {
5+
return "some post id";
6+
}
7+
}));
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
export class PostQueryBuilder {
3+
build() {
4+
return "SUPER * QUERY";
5+
}
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {Service} from "../../src/decorators/Service";
2+
3+
export const PostRepository = Service(() => ({
4+
getName() {
5+
return "hello from post repository";
6+
}
7+
}));
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {Container} from "../../src";
2+
import {PostController} from "./PostController";
3+
4+
const postController = Container.get(PostController);
5+
console.log(postController);

src/Container.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {ServiceMetadata} from "./types/ServiceMetadata";
2-
import {ObjectType} from "./types/ObjectType";
3-
import {Handler} from "./types/Handler";
1+
import {ContainerInstance} from "./ContainerInstance";
42
import {Token} from "./Token";
3+
import {Handler} from "./types/Handler";
4+
import {ObjectType} from "./types/ObjectType";
55
import {ServiceIdentifier} from "./types/ServiceIdentifier";
6-
import {ContainerInstance} from "./ContainerInstance";
6+
import {ServiceMetadata} from "./types/ServiceMetadata";
77

88
/**
99
* Service container.
@@ -97,7 +97,13 @@ export class Container {
9797
* Retrieves the service with given name or type from the service container.
9898
* Optionally, parameters can be passed in case if instance is initialized in the container for the first time.
9999
*/
100-
static get<T>(identifier: ServiceIdentifier): T {
100+
static get<T>(service: { service: T }): T;
101+
102+
/**
103+
* Retrieves the service with given name or type from the service container.
104+
* Optionally, parameters can be passed in case if instance is initialized in the container for the first time.
105+
*/
106+
static get<T>(identifier: ServiceIdentifier<T>): T {
101107
return this.globalInstance.get(identifier as any);
102108
}
103109

src/ContainerInstance.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import {ServiceMetadata} from "./types/ServiceMetadata";
2-
import {ObjectType} from "./types/ObjectType";
1+
import {Container} from "./Container";
2+
import {MissingProvidedServiceTypeError} from "./error/MissingProvidedServiceTypeError";
3+
import {ServiceNotFoundError} from "./error/ServiceNotFoundError";
34
import {Token} from "./Token";
5+
import {ObjectType} from "./types/ObjectType";
46
import {ServiceIdentifier} from "./types/ServiceIdentifier";
5-
import {ServiceNotFoundError} from "./error/ServiceNotFoundError";
6-
import {MissingProvidedServiceTypeError} from "./error/MissingProvidedServiceTypeError";
7-
import {Container} from "./Container";
7+
import {ServiceMetadata} from "./types/ServiceMetadata";
88

99
/**
1010
* TypeDI can have multiple containers.
@@ -90,7 +90,13 @@ export class ContainerInstance {
9090
* Retrieves the service with given name or type from the service container.
9191
* Optionally, parameters can be passed in case if instance is initialized in the container for the first time.
9292
*/
93-
get<T>(identifier: ServiceIdentifier): T {
93+
get<T>(id: { service: T }): T;
94+
95+
/**
96+
* Retrieves the service with given name or type from the service container.
97+
* Optionally, parameters can be passed in case if instance is initialized in the container for the first time.
98+
*/
99+
get<T>(identifier: ServiceIdentifier<T>): T {
94100

95101
const globalContainer = Container.of(undefined);
96102
let service = globalContainer.findService(identifier);
@@ -174,12 +180,15 @@ export class ContainerInstance {
174180
if (typeof identifierOrServiceMetadata === "string" || identifierOrServiceMetadata instanceof Token) {
175181
return this.set({ id: identifierOrServiceMetadata, value: value });
176182
}
183+
if (typeof identifierOrServiceMetadata === "object" && (identifierOrServiceMetadata as { service: Token<any> }).service) {
184+
return this.set({ id: (identifierOrServiceMetadata as { service: Token<any> }).service, value: value });
185+
}
177186
if (identifierOrServiceMetadata instanceof Function) {
178187
return this.set({ type: identifierOrServiceMetadata, id: identifierOrServiceMetadata, value: value });
179188
}
180189

181190
// const newService: ServiceMetadata<any, any> = arguments.length === 1 && typeof identifierOrServiceMetadata === "object" && !(identifierOrServiceMetadata instanceof Token) ? identifierOrServiceMetadata : undefined;
182-
const newService: ServiceMetadata<any, any> = identifierOrServiceMetadata;
191+
const newService: ServiceMetadata<any, any> = identifierOrServiceMetadata as any;
183192
const service = this.findService(newService.id);
184193
if (service && service.multiple !== true) {
185194
Object.assign(service, newService);
@@ -234,8 +243,15 @@ export class ContainerInstance {
234243
*/
235244
private findService(identifier: ServiceIdentifier): ServiceMetadata<any, any>|undefined {
236245
return this.services.find(service => {
237-
if (service.id)
246+
if (service.id) {
247+
if (identifier instanceof Object &&
248+
service.id instanceof Token &&
249+
(identifier as any).service instanceof Token) {
250+
return service.id === (identifier as any).service;
251+
}
252+
238253
return service.id === identifier;
254+
}
239255

240256
if (service.type && identifier instanceof Function)
241257
return service.type === identifier; // todo: not sure why it was here || identifier.prototype instanceof service.type;
@@ -270,6 +286,9 @@ export class ContainerInstance {
270286

271287
} else if (identifier instanceof Function) {
272288
type = identifier;
289+
290+
// } else if (identifier instanceof Object && (identifier as { service: Token<any> }).service instanceof Token) {
291+
// type = (identifier as { service: Token<any> }).service;
273292
}
274293

275294
// if service was not found then create a new one and register it
@@ -300,7 +319,7 @@ export class ContainerInstance {
300319
value = (this.get(service.factory[0]) as any)[service.factory[1]](...params);
301320

302321
} else { // regular factory function
303-
value = service.factory(...params);
322+
value = service.factory(...params, this);
304323
}
305324

306325
} else { // otherwise simply create a new object instance

0 commit comments

Comments
 (0)