dependency injection in typescript using di-injectable library
Published: 2024-06-29 19:35:43

In this article, we’ll explore the concept of dependency injection in TypeScript and how it can revolutionize our software development process using di-injectable library.

## What is Dependency Injection?

Dependency injection is a design pattern that allows us to decouple components by injecting their dependencies from external sources rather than creating them internally. This approach promotes loose coupling, reusability, and testability in our codebase.

## Constructor Injection

Constructor injection is one of the most common forms of dependency injection. It involves injecting dependencies through a class’s constructor. Let’s consider an example:

```typescript
class UserService {
  constructor(private userRepository: UserRepository) {}

  getUser(id: string) {
    return this.userRepository.getUserById(id);
  }
}

class UserRepository {
  getUserById(id: string) {
    // Retrieve user from the database
  }
}

const userRepository = new UserRepository();
const userService = new UserService(userRepository);
```

In the above example, the `UserService` class depends on the `UserRepository` class. By passing an instance of `UserRepository` through the constructor, we establish the dependency between the two classes. This approach allows for easy swapping of different implementations of `UserRepository`, making our code more flexible and extensible.


## Benefits of Dependency Injection

By embracing dependency injection, we unlock several benefits that greatly enhance our codebase:

- Loose Coupling
Dependency injection promotes loose coupling between components, as they depend on abstractions rather than concrete implementations. This enables us to swap out dependencies easily, facilitating code maintenance and scalability.

- Reusability
With dependency injection, we can create components with minimal dependencies, making them highly reusable in different contexts. By injecting specific implementations of dependencies, we can tailor the behavior of a component without modifying its code.

- Testability
Dependency injection greatly simplifies unit testing. By injecting mock or fake dependencies during testing, we can isolate components and verify their behavior independently. This leads to more reliable and maintainable test suites.

- Flexibility and Extensibility
Using dependency injection allows us to add new features or change existing ones without modifying the core implementation. By injecting new dependencies or modifying existing ones, we can extend the functionality of our codebase without introducing breaking changes.

## lets make dependency injection easier by using DI-injectable library
 DI-injectable library is a simple Dependency Injection (DI) library for TypeScript supporting Singleton and Transient service lifetimes.

## Installation

First, install the package via npm or yarn:

```sh

npm install injectable

yarn add injectable

```


## Usage

# Setting Up Services

-  Define Services: Create your service classes and use the `@Injectable` decorator and use `ServiceLifetime` enum to register your services as Singleton or Transient..
- Resolve Services: Use the `ServiceProvider` to resolve instances of your services.


## Example
Let's walk through a complete example.

1. Define Services
Create some simple services and use the @Injectable decorator.

```typescript
// src/services/logger.ts
import { Injectable } from 'di-injectable'; 

@Injectable(ServiceLifetime.Singleton)
export class Logger {
  log(message: string) {
    console.log(`Logger: ${message}`);
  }
}
``` 
```typescript
// src/services/userService.ts
import { Injectable, Inject } from 'di-injectable';
import { Logger } from './logger';

@Injectable()
export class UserService {
  constructor(@Inject(Logger) private logger: Logger) {}

  getUser() {
    this.logger.log('Getting user...');
    return { id: 1, name: 'John Doe' };
  }
}
```




2. Resolve Services
Use the `ServiceProvider` to resolve instances of your services.

```typescript
// src/app.ts
import { ServiceProvider } from 'di-injectable';
import { UserService } from './services/userService';

const serviceProvider = new ServiceProvider();

const userService = serviceProvider.resolve<UserService>(UserService);
const user = userService.getUser();
console.log(user);
```

## Explanation

- Defining Services:
  - The Logger service is a simple logger class.
  - The `UserService` class depends on the Logger service. The `@Inject` decorator is used to inject the Logger service 
  - into the `UserService` constructor.
- Registering Services:
  - We register the Logger service as a Singleton, meaning only one instance of Logger will be created and shared.
  - We register the `UserService` as a Transient by default, meaning a new instance of `UserService` will be created every time it is resolved.
- Resolving Services:
  - We create a `ServiceProvider` instance.
  - We resolve an instance of `UserService` using the `serviceProvider`.
The `UserService` will have the Logger instance injected into it due to the `@Inject` decorator.


## Service Lifetimes
- Singleton: Only one instance of the service is created and shared.
- Transient: A new instance of the service is created every time it is requested.


### API Reference


- ServiceProvider:
  - `resolve<T>(token: any): T`: Resolves an instance of the service.
  - `Injectable`: Decorator to mark a class as injectable as register it.
  - `Inject`: Decorator to inject dependencies into the constructor.



## Conclusion
Dependency injection is a powerful technique that improves code maintainability, testability, and flexibility. By leveraging constructor or property injection, we can create loosely coupled components that are highly reusable and easy to test.

As software engineers, embracing dependency injection in our TypeScript projects empowers us to write cleaner, more modular, and robust code. It enhances the scalability of our applications, enables efficient collaboration between team members, and simplifies the introduction of new features or changes.