## 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()`方法完全重置容器。这将有效地从容器中删除所有的注册服务。