/interject

PoC for flawless and framework-agnostic dependency injection within any TypeScript environment.

Primary LanguageTypeScriptMIT LicenseMIT

Build Status Coverage Status

Idea and use-case

This framework is inspired by Angular's dependency injection concept but it's far more lightweight and smaller without all the features and not exactly the same regarding the usage and concept.

Core principals

In most frontend application you'll have multiple dependencies, and sometimes you also have kinda like "circular" or "multi-depth" dependencies which can't be resolved or conflicts inside your application build. Sometimes you also just want a simple possibility to manage singletons, factories and values you need in different parts of the application without caring about how to distribute or allocate them. The Interject library will help you managing your dependencies with ease!

The following examples are written with TypeScript and decorators, if you plan to just use the most 'barebone' version make sure you checkout the Interject API for using the core as it is.

Definining injectables

Each injectable is defined with a key. The key is not only a string, it's rather a token. Inside the interject framework they're called InjectionToken. The key can also be any type of constructable class (not object!). Dependencies defined by a token are registered inside an Injector which then can be provided by a method called get. The dependency will automatically be instanciated/called/exposed by the injector and provided to your module.

TypeScript

import { Injectable, InjectionToken } from '@interject/core';

@Injectable()
class InjectableService {}

export const injectableValue = {
    provide: new InjectionToken<string>('injectable value token'),
    useValue: 'Hello there!'
}

JavaScript

const { Injectable, InjectionToken } = require('@interject/core');

class InjectableService {
    someMethod(): { a: 10 } {
        return { a: 10 };
    };
}

exports.InjectableService = {
    provide: InjectableService,
    useClass: InjectableService
};

exports.injectableValue = {
    provide: new InjectionToken<string>('injectable value token'),
    useValue: 'Hello there!'
};

Defining modules

Each module defines an application entrypoint. The module than can be bootstrapped via the bootstrap method exposed by the core. This will resolve all providers and subproviders for each injectable and return the module with all providers needed. In the background, modules simply manage Injectors inside which manage you dependencies and inject it into the constructor of the class - which you don't have to use if you don't want to (see: JavaScript)!

TypeScript

import { Module, bootstrap } from '@interject/core';

@Module({
    providers: [
        InjectableService, // taken from above
        { provide: new InjectionToken<string>('test'), useValue: 'test' }
    ]
})
class MyModule {
    constructor(private test: string) {}
}

const inst = bootstrap(MyModule);
console.log(inst.test); // 'test'

JavaScript

const { CommonInjector, InjectionToken } = require('@interject/core');
const InjectableService = require('./InjectableService');

const testToken = new InjectionToken<string>('test')

const injector = CommonInjector.create(
    providers: [
        InjectableService, // taken from above
        { provide: testToken, useValue: 'test' }
    ]
});

console.log(injector.get(testToken)); // 'test'
console.log(injector.get(InjectableService).someMethod); // { a: 10 }

Providers

There are a few provider-types you should be aware of. Each providers is built for a certain use-case. You'll find a detailed description of each below.

If you're interested in building plugins for the Interject framework, you might checkout the Metadata Reflection API as interject heavily uses it for saving injection information on classes/objects. Please referr to the Metadata Documentation for further reading.

FAQs

What kind of dependencies can be injected?

Please referr to the documentation of the different providers where you can see what's supported and what not.

Is it also compatible with JavaScript?

In theory; yes ... it's currently built on top of the reflect-metadata package. This means the main decorator part which makes this framework as flawless as it is, is only usable within TypeScript. But you can also call the decorators via JavaScript - unhandy but works! However, we recommend you using TypeScript for this framework. We recommend you using the straight CommonInjector and InjectionToken API within pure JavaScript application.