Skip to content

Instantly share code, notes, and snippets.

@dimitrilahaye
Last active February 7, 2023 07:04
Show Gist options
  • Save dimitrilahaye/9207ff38d4e2c732e0e32c779728c89b to your computer and use it in GitHub Desktop.
Save dimitrilahaye/9207ff38d4e2c732e0e32c779728c89b to your computer and use it in GitHub Desktop.
[Typescript] Usage of Dependency Injection with tsrynge
// example taken from https://blog.logrocket.com/dependency-inversion-principle-typescript
// all in one file
/**
* "reflect-metadata"
* This is a [Polyfill](https://developer.mozilla.org/fr/docs/Glossary/Polyfill) allowing
* to add the system of [reflection](https://fr.wikipedia.org/wiki/R%C3%A9flexion_(informatique))
* to javascript.
* This is a necessary lib for all dependency injection questions (with "tsrynge")
* which uses the principle of reflection to operate.
*
* ```bash
* yarn add reflect-metadata
* npm install --save reflect-metadata
* ```
*/
import 'reflect-metadata';
/**
* "tsrynge"'s container
* In dependency injection systems, the container is the module that will instantiate each
* class and nest them inside each other depending on how it was configured.
* This injection is done at runtime.
* [Documentation](https://github.com/microsoft/tsyringe#container)
*
* "injectable"
* Indicates to "tsrynge" which class will be injected
*
* "inject"
* Asks to "tsrynge" to inject something into a class
*
* ```bash
* yarn add tsrynge
* npm install --save tsrynge
* ```
*/
import { container, inject, injectable } from 'tsyringe';
/**
* Injection tokens used to configure dependency injection via "tsyringe"
*/
const tokens = {
PaymentProcessor: Symbol('PaymentProcessor')
} as const;
// High-level module
class ShoppingCartService {
constructor(
// this decorator will indicate to tsrynge where to inject stuff
@inject(tokens.PaymentProcessor) private paymentProcessor: PaymentProcessor,
) {}
checkout(cart: ShoppingCart) {
return this.paymentProcessor.processPayment(cart);
}
}
// Abstraction
interface PaymentProcessor {
processPayment(cart: ShoppingCart): boolean;
}
// Implementation of the abstraction
@injectable() // this decorator will indicate to tsrynge that this class will be injected
class StripePaymentProcessor implements PaymentProcessor {
processPayment(cart: ShoppingCart): boolean {
// Use the Stripe API to process the payment for the items in the shopping cart
}
}
// Configure injection container to tell him which concrete class will be injected according to which token
container.register(tokens.PaymentProcessor, { useClass: StripePaymentProcessor });
// This one-liner is equivalent of
const paymentProcessor = new PayPalPaymentProcessor();
const shoppingCartService = new ShoppingCartService(paymentProcessor);
// Now the ShoppingCartService depends on the abstraction, not the implementation
// following files show how to implement it with good practices
// at the root of your project
import 'reflect-metadata';
import { container } from 'tsyringe';
export default const tokens = {
PaymentProcessor: Symbol('PaymentProcessor')
} as const;
container.register(tokens.PaymentProcessor, { useClass: StripePaymentProcessor });
export default interface PaymentProcessor {
processPayment(cart: ShoppingCart): boolean;
}
import { inject } from 'tsyringe';
import tokens from 'ioc';
import PaymentProcessor from 'payment-processor.interface.ts';
export default class ShoppingCartService {
constructor(
@inject(tokens.PaymentProcessor) private paymentProcessor: PaymentProcessor,
) {}
checkout(cart: ShoppingCart) {
return this.paymentProcessor.processPayment(cart);
}
}
import { injectable } from 'tsyringe';
@injectable()
export default class StripePaymentProcessor implements PaymentProcessor {
processPayment(cart: ShoppingCart): boolean {
// Use the Stripe API to process the payment for the items in the shopping cart
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment