## Typecript的用法
使用TypeDI,你可以使用一個命名的服務。比如:
```
import { Container, Service, Inject } from 'typedi';
interface Factory {
create(): void;
}
@Service({ id: 'bean.factory' })
class BeanFactory implements Factory {
create() {}
}
@Service({ id: 'sugar.factory' })
class SugarFactory implements Factory {
create() {}
}
@Service({ id: 'water.factory' })
class WaterFactory implements Factory {
create() {}
}
@Service({ id: 'coffee.maker' })
class CoffeeMaker {
beanFactory: Factory;
sugarFactory: Factory;
@Inject('water.factory')
waterFactory: Factory;
constructor(@Inject('bean.factory') beanFactory: BeanFactory, @Inject('sugar.factory') sugarFactory: SugarFactory) {
this.beanFactory = beanFactory;
this.sugarFactory = sugarFactory;
}
make() {
this.beanFactory.create();
this.sugarFactory.create();
this.waterFactory.create();
}
}
let coffeeMaker = Container.get<CoffeeMaker>('coffee.maker');
coffeeMaker.make();
```
如果你想存儲(并在以后注入)一些設置或配置選項,這個功能特別有用。比如說:
```
import { Container, Service, Inject } from 'typedi';
// 在你的全局應用參數中的某個地方
Container.set('authorization-token', 'RVT9rVjSVN');
@Service()
class UserRepository {
@Inject('authorization-token')
authorizationToken: string;
}
```
當你寫測試時,你可以很容易地使用`set`方法為你測試的類提供你自己的 "假 "依賴:`provide `容器的方法。
```
Container.set(CoffeeMaker, new FakeCoffeeMaker());
// 或者對于命名的服務
Container.set([
{ id: 'bean.factory', value: new FakeBeanFactory() },
{ id: 'sugar.factory', value: new FakeSugarFactory() },
{ id: 'water.factory', value: new FakeWaterFactory() },
]);
```
*****
## TypeScript 高級使用范例
* [使用工廠函數創建服務
](#使用工廠函數創建服務)
* [使用工廠類來創建服務
](#使用工廠類來創建服務)
* [循環引用的問題](#循環引用的問題)
* [自定義裝飾器](#自定義裝飾器)
* [使用服務組
](#使用服務組
)
* [使用多個容器和作用域容器
](#使用多個容器和作用域容器
)
* [移除已注冊的服務或重置容器狀態](#移除已注冊的服務或重置容器狀態)
### 使用工廠函數創建服務
你可以使用工廠函數在容器中創建你的服務。
這樣,服務實例將通過調用你的工廠函數而不是直接實例化一個類來創建。
```
import { Container, Service } from 'typedi';
function createCar() {
return new Car('V8');
}
@Service({ factory: createCar })
class Car {
constructor(public engineType: string) {}
}
// 從容器中獲取服務
// 服務將通過調用指定的工廠函數來創建
const car = Container.get(Car);
console.log(car.engineType); // > "V8"
```
### 使用工廠類來創建服務
你也可以使用工廠類來創建你的服務。
這樣,服務實例將通過調用給定的工廠服務的方法工廠來創建,而不是直接實例化一個類
```
import { Container, Service } from 'typedi';
@Service()
class CarFactory {
constructor(public logger: LoggerService) {}
create() {
return new Car('BMW', this.logger);
}
}
@Service({ factory: [CarFactory, 'create'] })
class Car {
constructor(public model: string, public logger: LoggerInterface) {}
}
```
### 循環引用的問題
在語言中,有一個已知的問題,它不能處理循環引用。比如說:
```
// Car.ts
@Service()
export class Car {
@Inject()
engine: Engine;
}
// Engine.ts
@Service()
export class Engine {
@Inject()
car: Car;
}
```
這段代碼不會工作,因為Engine有一個對Car的引用,而Car有一個對Engine的引用。其中一個將是未定義的,這會導致錯誤。為了解決這個問題,你需要在一個函數中這樣指定一個類型。
```
// Car.ts
@Service()
export class Car {
@Inject(type => Engine)
engine: Engine;
}
// Engine.ts
@Service()
export class Engine {
@Inject(type => Car)
car: Car;
}
```
就這樣了。構造函數的注入也是如此。
### 自定義裝飾器
你可以創建你自己的裝飾器,它將為你的服務依賴性注入你給定的值。比如說:
```
// LoggerInterface.ts
export interface LoggerInterface {
log(message: string): void;
}
// ConsoleLogger.ts
import { LoggerInterface } from './LoggerInterface';
export class ConsoleLogger implements LoggerInterface {
log(message: string) {
console.log(message);
}
}
// Logger.ts
export function Logger() {
return function (object: Object, propertyName: string, index?: number) {
const logger = new ConsoleLogger();
Container.registerHandler({ object, propertyName, index, value: containerInstance => logger });
};
}
// UserRepository.ts
@Service()
export class UserRepository {
constructor(@Logger() private logger: LoggerInterface) {}
save(user: User) {
this.logger.log(`user ${user.firstName} ${user.secondName} has been saved.`);
}
}
```
### 使用服務組
你可以將多個服務分組到一個以服務ID或token標記的組中。比如說:
```
// Factory.ts
export interface Factory {
create(): any;
}
// FactoryToken.ts
export const FactoryToken = new Token<Factory>('factories');
// BeanFactory.ts
@Service({ id: FactoryToken, multiple: true })
export class BeanFactory implements Factory {
create() {
console.log('bean created');
}
}
// SugarFactory.ts
@Service({ id: FactoryToken, multiple: true })
export class SugarFactory implements Factory {
create() {
console.log('sugar created');
}
}
// WaterFactory.ts
@Service({ id: FactoryToken, multiple: true })
export class WaterFactory implements Factory {
create() {
console.log('water created');
}
}
// app.ts
// 現在你可以在一個數組中獲得所有的工廠了
Container.import([BeanFactory, SugarFactory, WaterFactory]);
const factories = Container.getMany(FactoryToken); // factories是Factory[]
factories.forEach(factory => factory.create());
```
### 使用多個容器和作用域容器
默認情況下,所有的服務都存儲在全局服務容器中,這個全局服務容器持有你的每個服務的所有唯一實例。
如果你想讓你的服務根據某些用戶上下文(例如http請求)以不同的方式行事并在里面存儲數據--你可以為不同的上下文使用不同的容器。比如說:
```
// QuestionController.ts
@Service()
export class QuestionController {
constructor(protected questionRepository: QuestionRepository) {}
save() {
this.questionRepository.save();
}
}
// QuestionRepository.ts
@Service()
export class QuestionRepository {
save() {}
}
// app.ts
const request1 = { param: 'question1' };
const controller1 = Container.of(request1).get(QuestionController);
controller1.save('Timber');
Container.reset(request1);
const request2 = { param: 'question2' };
const controller2 = Container.of(request2).get(QuestionController);
controller2.save('');
Container.reset(request2);
```
在這個例子中,`controller1`和`controller2`是完全不同的實例,這些控制器中使用的`QuestionRepository`也是不同的實例。
`Container.reset`刪除了帶有給定上下文標識符的容器。如果你想讓你的服務完全是全局的,而不是針對容器的,你可以把它們標記為全局的。
```
@Service({ global: true })
export class QuestionUtils {}
```
而這個全局服務將是所有容器中的同一個實例。
TypeDI也支持函數依賴性注入。下面是例子:
```
export const PostRepository = Service(() => ({
getName() {
return 'hello from post repository';
},
}));
export const PostManager = Service(() => ({
getId() {
return 'some post id';
},
}));
export class PostQueryBuilder {
build() {
return 'SUPER * QUERY';
}
}
export const PostController = Service(
[PostManager, PostRepository, PostQueryBuilder],
(manager, repository, queryBuilder) => {
return {
id: manager.getId(),
name: repository.getName(),
query: queryBuilder.build(),
};
}
);
const postController = Container.get(PostController);
console.log(postController);
```
### 刪除注冊的服務或重置容器狀態
如果你需要從容器中移除注冊的服務,只需使用`Container.remove(...)`方法。你也可以通過調用`Container.reset()`方法完全重置容器。這將有效地從容器中刪除所有的注冊服務。