JavaScript has no syntax that supports this mapping. Even if a custom function parser was written to provide the desired behavior for destructed parameters, such as function({lodash:_}) ... , it would not work for overloaded functions, which is a serious drawback. The easiest way to handle this is
function foo(lodash){ const _ = lodash; ... }
And this obviously will not work for invalid variable names like lodash.pick .
A common practice for DI recipes for this is to provide annotations. All annotations described can be combined together. They are especially implemented in Angular DI . Angular injector is available for standalone use (including Node) as injection-js library.
Annotation property
Thus, the signature of the function and the list of dependencies should not match. This recipe can be seen in action in AngularJS.
The property contains a list of DI tokens. They can be dependency names that will be loaded using require or something else.
And DI runs like
const fnArgs = require('fn-args'); const annotation = foo[ANNOTATION] || fnArgs(foo); foo(...annotation.map(depName => require(depName));
This annotation style allows you to use function definitions because lifting allows you to place a note over the function signature for convenience.
Array Annotations
The signature of the function and the list of dependencies must not match. This recipe can be seen in AngularJS.
When a function is represented as an array, this means that it is an annotated function, and its parameters should be treated as annotations, and the latter itself.
const foo = [ 'lodash', function foo(_) { ... } ]; ... const fn = foo[foo.length - 1]; const annotation = foo.slice(0, foo.length - 1); foo(...annotation.map(depName => require(depName));
TypeScript annotation
This recipe can be seen in Angular (2 and above) and relies on TypeScript types. Types can be extracted from the constructor signature and used for DI. Possible are the Reflect metadata clause and TypeScript emitDecoratorMetadata .
Released constructor types are saved as metadata for the respective classes and can be obtained using the Reflect API to resolve dependencies. This is a DI class , since decorators are only supported in classes, it works best with DI containers:
import 'core-js/es7/reflect'; abstract class Dep {} function di(target) { } @di class Foo { constructor(dep: Dep) { ... } } ... const diContainer = { Dep: require('lodash') }; const annotations = Reflect.getMetadata('design:paramtypes', Foo); new (Foo.bind(Foo, ...annotations.map(dep => diContainer [dep]))();
This will create workable JS code, but will create type problems since the Lodash object is not an instance of the Dep token class. This method is primarily effective for class dependencies that are introduced into classes.
For non-classic DI, backup to other annotations is required.