Search code examples
node.jstypescriptdependency-injectioninversifyjs

typescript dependency injection with little footprint


I am new to nodejs and typescript, coming from C#. I want to use dependency injection in my project and found that the most popular package is inversify.

I started using it but I don't like the fact that I have to add decorators all over.

for example it bothers me that I need to add @inject before parameters in the constructor:

public constructor(
    @inject(TYPES.Weapon) katana: Weapon,
    @inject(TYPES.ThrowableWeapon) shuriken: ThrowableWeapon
)

This mean every class has to know the TYPES object...

I don't understand why @inject needs the string literal and can't inject just on the basis of the type...

Is there a neater way to do this?


Solution

  • In contrast to strictly typed languages, TypeScript types don't exist at runtime. It's possible to use type information at runtime for dependency injection but in limited ways. With the use of emitDecoratorMetadata TypeScript option it's possible to get constructor parameter types and use them for DI.

    The example is injection-js, which is Angular injector that was extracted from the library for standalone use. Only @Injectable decorator is needed to be used on DI-enabled class, @Inject parameter decorators are optional.

    The limitations are that only class instances can be injected this way:

    constructor(foo: FooClass) {}
    

    Generics are ignored and discarded:

    constructor(foo: FooClass<Bar>) {}
    

    Other types are ignored and result in DI error:

    constructor(foo: fooSymbol) {}
    
    constructor(foo: 'foo string provider') {}
    

    The same applies to InversifyJS:

    In case of concrete injections, you can simply define your constructor parameters as usual without using the @inject decorator.

    InversifyJS also supports TypeScript's constructor assignments so you can have private or protected access modifiers in your parameters and the container will have no trouble injecting the dependencies

    It would be possible to omit @inject for Weapon and ThrowableWeapon if they were classes but in listed example TYPES.Weapon is a symbol and Weapon is an interface that doesn't exist at runtime, so @inject is necessary.