0

Is there any dependency injection container like InversifyJs but for functional programming in typescript? What I want to achieve is to inject a fake function from my tests end to end in the same way that i do with the OOP like the following example

Test end to end binding fake user repository

import magicContainer from 'magic-library'
import { UserRepositoryInterface } from '..'
import { FakeUserRepository } from '..'

describe(‘My test describe’, () => {

    test('My test', async () => {
        magicContainer.bind(UserRepositoryInterface, FakeUserRepository)

        await supertest(app)
        .post('/my-endpoint')
        .send({payload})
        .expect(HttpStatus.OK)
})

})

Express Router:

import {UserRepositoryInterface} from '...'
import {UserRepository} from '...'
import magicContainer from ‘magic-library’

const router = Router()
router.post('/my-endpoint', magicContainer.bind(UserRepositoryInterface, UserRepository)
)

User Interface and implementations:

export interface UserRepositoryInterface {
    myFunction: () => Promise<User[]>
}


export const UserRepository: UserRepositoryInterface = {
    myFunction: async () => {
        …
    }
}

export const FakeUserRepository: UserRepositoryInterface = {
    myFunction: async () => {
        …
    }
}

I have tried with libraries like InversifyJS, tsyringe and node-dependency-injection but they are purely object oriented, not for functional programming

3
  • I don't have experience with typescript. Dependency Injection is just a design pattern mostly used in OOP. You have a class, you have some dependencies on other classes that you general "inject" during the construction of the object. The dependencies could be passed in the constructor, using a builder pattern, a decorator, etc. In functional programming, you don't have exactly dependencies. You have a function that receives parameters (could be values or other functions) and you return a value or a function. Pass a function as a parameter could be similar to dependency injection Commented May 10, 2023 at 13:59
  • You might find some luck with iti npmjs.com/package/iti Disclaimer: author here :) Commented May 15, 2023 at 14:26
  • Since dependency injection in FP is just passing arguments to functions, I find there's no real need for a library to do this. I just instantiate functions with mock dependencies manually in tests Commented Jul 5, 2023 at 12:09

1 Answer 1

1

I faced the same problem before, some people argue that dependency injection is a design pattern that should be implemented in OOP only however this is not right, dependency injection and dependency inversion are principles can be applied on both functional programming and OOP The idea is very simple but first we should know the main components for dependency injection:

  • dependency
  • dependency consumer
  • DC/dependency container Regarding the dependency consumer, in OOP you have a constructor that you inject dependencies in however in FP you don't have that so you just need to inject a function inside a function, how can you do that? Thanks to javascript closures and higher order functions, using them you can implement a higher order function that receives the dependencies you need and returns a function that holds the concrete implementation so basically the higher-order function plays the constructor role. The third thing you need is the dependency injection container and this can be done using some data structure and algorithm to hold all your dependencies and pick the right one when it's required. I created a library for that purpose you can check it, I am using it currently on production and works very well https://github.com/tareksalem/injectx

Example:

1- create a dependency:

export class DB {
  users = [
    {
      username: "John",
    }
  ]
}
export const db = new DB();

2- Create a Dependency Consumer

export const GetUserRepository = ({ db }: { db: DB }) => (username: string) => {
    return db.users.find(user => user.username === username)
}
export const getUserReposistory = InjectIn(GetUserRepository)

3- bind the dependencies into the container

import { GetContainer } from "injectx"
import { db } from "./db"
import { GetUserRepository } from "./getUser.repository";

export const DiInit = () => {
  GetContainer('default').Bind(db, { name: 'db' }).Bind(GetUserRepository);
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.