Skip to content

Commit 918b05d

Browse files
author
Umed Khudoiberdiev
committed
Added javascript support, removed "require decorator" things , added injectmany decorator, fixed issues #47 #48 #51
1 parent 905f3ec commit 918b05d

File tree

15 files changed

+303
-105
lines changed

15 files changed

+303
-105
lines changed

CHANGELOG.md

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

3+
## 0.7.0
4+
5+
* added javascript support
6+
* removed deprecated `@Require` decorator
7+
* added support for transient services
8+
* now service constructors cannot accept non-service arguments
9+
* added `@InjectMany` decorator to support injection of "many" values
10+
* fixed the way how global services work
11+
312
## 0.6.1
413

514
* added `Container.has` method

README.md

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,107 @@
55
[![Dependency Status](https://david-dm.org/typestack/typedi.svg)](https://david-dm.org/typestack/typedi)
66
[![Join the chat at https://gitter.im/typestack/typedi](https://badges.gitter.im/typestack/typedi.svg)](https://gitter.im/typestack/typedi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
77

8-
TypeDI is a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) tool for TypeScript.
8+
TypeDI is a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) tool for JavaScript and TypeScript.
99
Using TypeDI you can build well-structured and easily tested applications.
1010

11-
## Installation
11+
## Usage with JavaScript
1212

13+
Install the module:
14+
15+
`npm install typedi --save`
16+
17+
Now you can use TypeDI.
18+
The most simple usage example is:
19+
20+
```javascript
21+
class SomeClass {
22+
23+
someMethod() {
24+
}
25+
26+
}
27+
28+
var Container = require("typedi");
29+
let someClass = Container.get(SomeClass);
30+
someClass.someMethod();
31+
```
32+
33+
Then you can call `Container.get(SomeClass)` from anywhere in your application
34+
and you'll always have the same instance of `SomeClass`.
35+
36+
In your class's constructor you always recieve as a last argument a container which you can use to get other dependencies.
37+
38+
```javascript
39+
class BeanFactory {
40+
create() {
41+
}
42+
}
43+
44+
class SugarFactory {
45+
create() {
46+
}
47+
}
48+
49+
class WaterFactory {
50+
create() {
51+
}
52+
}
53+
54+
class CoffeeMaker {
55+
56+
constructor(container) {
57+
this.beanFactory = container.get(BeanFactory);
58+
this.beanFactory = container.get(SugarFactory);
59+
this.beanFactory = container.get(WaterFactory);
60+
}
61+
62+
make() {
63+
this.beanFactory.create();
64+
this.sugarFactory.create();
65+
this.waterFactory.create();
66+
}
67+
68+
}
69+
70+
var container = require("typedi");
71+
var coffeeMaker = container.get(CoffeeMaker);
72+
coffeeMaker.make();
73+
```
74+
75+
## Usage with TypeScript
1376

1477
1. Install module:
1578

1679
`npm install typedi --save`
1780

18-
2. You also need to install [reflect-metadata](https://www.npmjs.com/package/reflect-metadata) package.
81+
2. Install [reflect-metadata](https://www.npmjs.com/package/reflect-metadata) package:
1982

2083
`npm install reflect-metadata --save`
21-
22-
and import it somewhere in the global place of your app (for example in `app.ts`):
23-
84+
85+
and import it somewhere in the global place of your app before any service declaration or import (for example in `app.ts`):
86+
2487
`import "reflect-metadata";`
2588

2689
3. You may need to install node typings:
2790

2891
`npm install @types/node --save`
29-
3092

31-
4. Also make sure you are using TypeScript compiler version > **2.1**
32-
and you have enabled following settings in `tsconfig.json`:
93+
94+
4. Enabled following settings in `tsconfig.json`:
3395

3496
```json
3597
"emitDecoratorMetadata": true,
3698
"experimentalDecorators": true,
3799
```
38100

39-
## Usage
40-
101+
Now you can use TypeDI.
41102
The most simple usage example is:
42103

43104
```typescript
44-
import {Container} from "typedi";
105+
import "reflect-metadata";
106+
import {Service, Container} from "typedi";
45107

108+
@Service()
46109
class SomeClass {
47110

48111
someMethod() {
@@ -56,24 +119,8 @@ someClass.someMethod();
56119

57120
Then you can call `Container.get(SomeClass)` from anywhere in your application
58121
and you'll always have the same instance of `SomeClass`.
59-
60-
If you want to use more advanced functionality you need to mark your class with `@Service` decorator:
61-
62-
```typescript
63-
import {Service} from "typedi";
64-
65-
@Service()
66-
class SomeClass {
67-
68-
someMethod() {
69-
}
70-
71-
}
72-
```
73-
74-
Its recommended to always use `@Service` decorator on your service classes.
75122

76-
You can services into your class using `@Inject` decorator:
123+
You can use **property injection** and inject services into your class using `@Inject` decorator:
77124

78125
```typescript
79126
import {Container, Inject, Service} from "typedi";

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "typedi",
3-
"version": "0.6.1",
3+
"version": "0.7.0",
44
"description": "Dependency injection for TypeScript",
55
"license": "MIT",
66
"readmeFilename": "README.md",

src/ContainerInstance.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,11 @@ export class ContainerInstance {
9191
* Optionally, parameters can be passed in case if instance is initialized in the container for the first time.
9292
*/
9393
get<T>(identifier: ServiceIdentifier): T {
94-
let service = this.findService(identifier);
9594

96-
// in the case if service was not found registered we search in the global container for this service
97-
if (!service) {
98-
const globalService = Container.of(undefined).findService(identifier);
99-
if (globalService && globalService.global === true)
100-
service = globalService;
101-
}
95+
const globalContainer = Container.of(undefined);
96+
let service = globalContainer.findService(identifier);
97+
if ((!service || service.global !== true) && globalContainer !== this)
98+
service = this.findService(identifier);
10299

103100
return this.getServiceValue(identifier, service);
104101
}
@@ -271,6 +268,7 @@ export class ContainerInstance {
271268
let params: any[] = paramTypes ? this.initializeParams(type, paramTypes) : [];
272269

273270
// if factory is set then use it to create service instance
271+
let value: any;
274272
if (service.factory) {
275273

276274
// filter out non-service parameters from created service constructor
@@ -281,10 +279,10 @@ export class ContainerInstance {
281279
if (service.factory instanceof Array) {
282280
// use special [Type, "create"] syntax to allow factory services
283281
// in this case Type instance will be obtained from Container and its method "create" will be called
284-
service.value = (this.get(service.factory[0]) as any)[service.factory[1]](...params);
282+
value = (this.get(service.factory[0]) as any)[service.factory[1]](...params);
285283

286284
} else { // regular factory function
287-
service.value = service.factory(...params);
285+
value = service.factory(...params);
288286
}
289287

290288
} else { // otherwise simply create a new object instance
@@ -293,19 +291,21 @@ export class ContainerInstance {
293291

294292
params.unshift(null);
295293

296-
// if there are no constructor parameters in the class then pass a container instance into it
294+
// "extra feature" - always pass container instance as the last argument to the service function
297295
// this allows us to support javascript where we don't have decorators and emitted metadata about dependencies
298296
// need to be injected, and user can use provided container to get instances he needs
299-
if (params.length === 1)
300-
params.push(this);
297+
params.push(this);
301298

302-
service.value = new (type.bind.apply(type, params))();
299+
value = new (type.bind.apply(type, params))();
303300
}
304301

302+
if (service && !service.transient && value)
303+
service.value = value;
304+
305305
if (type)
306-
this.applyPropertyHandlers(type, service.value);
306+
this.applyPropertyHandlers(type, value);
307307

308-
return service.value;
308+
return value;
309309
}
310310

311311
/**

src/decorators/InjectMany.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {Container} from "../Container";
2+
import {Token} from "../Token";
3+
import {CannotInjectError} from "../error/CannotInjectError";
4+
5+
/**
6+
* Injects a service into a class property or constructor parameter.
7+
*/
8+
export function InjectMany(type?: (type?: any) => Function): Function;
9+
10+
/**
11+
* Injects a service into a class property or constructor parameter.
12+
*/
13+
export function InjectMany(serviceName?: string): Function;
14+
15+
/**
16+
* Injects a service into a class property or constructor parameter.
17+
*/
18+
export function InjectMany(token: Token<any>): Function;
19+
20+
/**
21+
* Injects a service into a class property or constructor parameter.
22+
*/
23+
export function InjectMany(typeOrName?: ((type?: any) => Function)|string|Token<any>): Function {
24+
return function(target: Object, propertyName: string, index?: number) {
25+
26+
if (!typeOrName)
27+
typeOrName = () => (Reflect as any).getMetadata("design:type", target, propertyName);
28+
29+
Container.registerHandler({
30+
object: target,
31+
propertyName: propertyName,
32+
index: index,
33+
value: containerInstance => {
34+
let identifier: any;
35+
if (typeof typeOrName === "string") {
36+
identifier = typeOrName;
37+
38+
} else if (typeOrName instanceof Token) {
39+
identifier = typeOrName;
40+
41+
} else {
42+
identifier = typeOrName();
43+
}
44+
45+
if (identifier === Object)
46+
throw new CannotInjectError(target, propertyName);
47+
48+
return containerInstance.getMany<any>(identifier);
49+
}
50+
});
51+
};
52+
}

src/decorators/Require.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/decorators/Service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ export function Service<T, K extends keyof T>(optionsOrServiceName?: ServiceOpti
3636
if (typeof optionsOrServiceName === "string" || optionsOrServiceName instanceof Token) {
3737
service.id = optionsOrServiceName;
3838
service.multiple = (optionsOrServiceName as ServiceOptions<T, K>).multiple;
39-
service.global = (optionsOrServiceName as ServiceOptions<T, K>).global;
39+
service.global = true;
40+
service.transient = (optionsOrServiceName as ServiceOptions<T, K>).transient;
4041

4142
} else if (optionsOrServiceName) { // ServiceOptions
4243
service.id = (optionsOrServiceName as ServiceOptions<T, K>).id;
4344
service.factory = (optionsOrServiceName as ServiceOptions<T, K>).factory;
4445
service.multiple = (optionsOrServiceName as ServiceOptions<T, K>).multiple;
45-
service.global = (optionsOrServiceName as ServiceOptions<T, K>).global;
46+
service.global = true;
47+
service.transient = (optionsOrServiceName as ServiceOptions<T, K>).transient;
4648
}
4749

4850
Container.set(service);

src/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
import {Container} from "./Container";
2+
13
export * from "./decorators/Service";
24
export * from "./decorators/Inject";
3-
export * from "./decorators/Require";
5+
export * from "./decorators/InjectMany";
46
export {Container} from "./Container";
57
export {ContainerInstance} from "./ContainerInstance";
68
export {Token} from "./Token";
79
export {Handler} from "./types/Handler";
810
export {ServiceOptions} from "./types/ServiceOptions";
911
export {ServiceIdentifier} from "./types/ServiceIdentifier";
1012
export {ServiceMetadata} from "./types/ServiceMetadata";
11-
export {ObjectType} from "./types/ObjectType";
13+
export {ObjectType} from "./types/ObjectType";
14+
15+
export default Container;

src/types/ServiceMetadata.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ export interface ServiceMetadata<T, K extends keyof T> {
1818
*/
1919
global?: boolean;
2020

21+
/**
22+
* Indicates if instance of this class must be created on each its request.
23+
* Global option is ignored when this option is used.
24+
*/
25+
transient?: boolean;
26+
2127
/**
2228
* Allows to setup multiple instances the different classes under a single service id string or token.
2329
*/

src/types/ServiceOptions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ export interface ServiceOptions<T, K extends keyof T> {
1212
*/
1313
global?: boolean;
1414

15+
/**
16+
* Indicates if instance of this class must be created on each its request.
17+
* Global option is ignored when this option is used.
18+
*/
19+
transient?: boolean;
20+
1521
/**
1622
* Allows to setup multiple instances the different classes under a single service id string or token.
1723
*/

0 commit comments

Comments
 (0)