TypeDI Documentation

repository·develop·Indexed 26 days ago

https://github.com/typestack/typedi

A dependency injection tool for TypeScript and JavaScript designed for building well-structured and testable applications in Node.js or the browser. It provides features such as constructor-based injection via the @Service decorator, named services using tokens or strings, scoped containers for context-specific services, and support for factory functions and classes. Version 0.10.0 requires reflect-metadata for metadata reflection.

Tokens
11.1K
Snippets
32
Records
58
Agent score
87%

What's inside TypeDI

  1. Use Service Tokens for type-safe container access

    develop

    Service tokens are unique identifiers that provide type-safe access to values stored in a Container. By using the Token<T> class, you ensure that the value retrieved from the container matches the expected type T at compile time.

    import 'reflect-metadata';
    import { Container, Token } from 'typedi';
    
    // Define a typed token
    export const JWT_SECRET_TOKEN = new Token<string>('MY_SECRET');
    
    // Set a value using the token
    Container.set(JWT_SECRET_TOKEN, 'wow-such-secure-much-encryption');
    
    // Retrieve the value type-safely
    const JWT_SECRET = Container.get(JWT_SECRET_TOKEN);
  2. Use constructor injection

    develop

    When a class is marked with the @Service decorator, TypeDI automatically infers and injects dependencies for every constructor argument without requiring the @Inject decorator. You can still use @Inject within the constructor if you need to overwrite the inferred type with a specific Token, service name, or constructable type.

    import 'reflect-metadata';
    import { Container, Inject, Service } from 'typedi';
    
    @Service()
    class InjectedExampleClass {
      print() {
        console.log('I am alive!');
      }
    }
    
    @Service()
    class ExampleClass {
      constructor(
        @Inject()
        public withDecorator: InjectedExampleClass,
        public withoutDecorator: InjectedExampleClass
      ) {}
    }
    
    const instance = Container.get(ExampleClass);
    
    // Both properties are automatically injected because the class is a @Service
    instance.withDecorator.print();
    instance.withoutDecorator.print();
  3. Provide fake dependencies for testing

    develop

    When writing tests, you can override real dependencies with mocks or fakes using the Container.set method. You can provide a single class dependency or an array of named service objects.

    // Replace a class dependency
    Container.set(CoffeeMaker, new FakeCoffeeMaker());
    
    // Replace multiple named services at once
    Container.set([
      { id: 'bean.factory', value: new FakeBeanFactory() },
      { id: 'sugar.factory', value: new FakeSugarFactory() },
      { id: 'water.factory', value: new FakeWaterFactory() },
    ]);
  4. Create custom decorators for dependency injection

    develop

    You can create custom decorators to inject specific values or service instances into your class dependencies. To do this, define a function that returns a decorator function. Inside the decorator, use Container.registerHandler to map the target object, propertyName, and index to a specific value or a factory function that returns the value.

    This is useful for injecting specialized loggers, configuration objects, or mock implementations into constructors.

    // 1. Define the custom decorator
    export function Logger() {
      return function (object: Object, propertyName: string, index?: number) {
        const logger = new ConsoleLogger();
        // Register the handler to inject the logger instance
        Container.registerHandler({
          object, 
          propertyName, 
          index, 
          value: containerInstance => logger 
        });
      };
    }
    
    // 2. Use the decorator in a class constructor
    @Service()
    export class UserRepository {
      constructor(@Logger() private logger: LoggerInterface) {}
    
      save(user: User) {
        this.logger.log(`user ${user.firstName} ${user.secondName} has been saved.`);
      }
    }
  5. Install TypeDI with TypeScript

    develop

    To use TypeDI in a TypeScript project, install typedi and reflect-metadata via npm. You must also ensure reflect-metadata is imported at the very first line of your application entry point to enable metadata reflection.

    npm install typedi reflect-metadata
    import 'reflect-metadata';
    
    // Your other imports and initialization code
  6. Inject dependencies using Constructor Argument Injection

    develop

    Any class marked with the @Service() decorator will have its constructor parameters automatically injected.

    Note: TypeDI automatically inserts the container instance as the last parameter in the constructor. Ensure your constructor arguments match the types of your registered dependencies.

    import 'reflect-metadata';
    import { Container, Inject, Service } from 'typedi';
    
    @Service()
    class InjectedClass {}
    
    @Service()
    class ExampleClass {
      constructor(public injectedClass: InjectedClass) {}
    }
    
    const instance = Container.get(ExampleClass);
    console.log(instance.injectedClass instanceof InjectedClass);
    // prints true
  7. Inject dependencies using Property Injection

    develop

    You can use the @Inject() decorator on class properties. When the parent class is initialized by TypeDI, the property will be automatically assigned the instance of the requested dependency.

    import 'reflect-metadata';
    import { Container, Inject, Service } from 'typedi';
    
    @Service()
    class InjectedClass {}
    
    @Service()
    class ExampleClass {
      @Inject()
      injectedClass: InjectedClass;
    }
    
    const instance = Container.get(ExampleClass);
    console.log(instance.injectedClass instanceof InjectedClass);
    // prints true
  8. Use named services for dependency injection

    develop

    TypeDI allows you to use string identifiers (named services) instead of class references. This is useful for injecting configuration, settings, or specific instances. Use Container.set(id, instance) to register a named service and container.get(id) to retrieve it.

    var Container = require('typedi').Container;
    
    class CoffeeMaker {
      constructor(container) {
        this.beanFactory = container.get('bean.factory');
      }
    }
    
    Container.set('bean.factory', new BeanFactory());
    
    var coffeeMaker = Container.get('coffee.maker');
  9. Configure service scope (Singleton vs Transient)

    develop

    By default, every registered service in TypeDI is a singleton, meaning Container.get(MyClass) always returns the same instance.

    To create a transient service (where a new instance is created every time it is requested), use the scope: 'transient' option within the @Service() decorator.

    import 'reflect-metadata';
    import { Container, Inject, Service } from 'typedi';
    
    @Service({ scope: 'transient' })
    class ExampleTransientClass {
      constructor() {
        console.log('I am being created!');
      }
    }
    
    const instanceA = Container.get(ExampleTransientClass);
    const instanceB = Container.get(ExampleTransientClass);
    
    console.log(instanceA !== instanceB);
    // prints true
  10. Integrate TypeDI with TypeORM and routing-controllers

    develop

    To enable dependency injection across your application when using TypeORM or routing-controllers, you must configure them to use the top-level TypeDI Container. This ensures that both the ORM and the routing layer resolve dependencies from the same container instance.

    import { useContainer as rcUseContainer } from 'routing-controllers';
    import { useContainer as typeOrmUseContainer } from 'typeorm';
    import { Container } from 'typedi';
    
    rcUseContainer(Container);
    typeOrmUseContainer(Container);