Skip to content

Inheritance support ​

Needle DI offers extensive support for inheritance, allowing for abstractions and interfaces.

Auto-binding using @injectable() ​

NOTE

Auto-binding with @injectable() has some limitations, see the note below.

Given the following class structure:

ts
abstract class ExampleService {
  /* ... */
}

@injectable()
class FooService extends ExampleService {
  /* ... */
}

@injectable()
class BarService extends ExampleService {
  /* ... */
}

This will automatically bind FooService and BarService, but it will also automatically bind two multi-providers for the token ExampleService:

ts
import { 
container
} from "./container";
import {
ExampleService
,
FooService
,
BarService
} from "./example.service";
const
fooService
=
container
.
get
(
FooService
);
const
barService
=
container
.
get
(
BarService
);
// Will be the same instances as "fooService" and "barService': const
myServices
=
container
.
get
(
ExampleService
, {
multi
: true });

IMPORTANT

If you inject something using a parent class as token, make sure your subclasses are referenced somewhere. Otherwise, the auto-binding might not work since their decorators are not invoked. Even worse, your subclasses might not even appear in your final bundle due to tree-shaking.

To prevent this, consider to register your subclasses explicitly using manual binding:

ts
container.bindAll(FooService, BarService);

Manual binding ​

If you bind something that has a parent class, a multi-provider for the parent class will be registered automatically.

Given the following example:

ts
import { 
Container
} from "@needle-di/core";
abstract class
ExampleService
{
/* ... */ } class
FooService
extends
ExampleService
{
/* ... */ } class
BarService
extends
ExampleService
{
/* ... */ } const
container
= new
Container
();
container
.
bindAll
(
FooService
,
BarService
,
);

The container will automatically register the following bindings internally:

ts
container.bindAll(
  {
    provide: ExampleService,
    useExisting: FooService,
    multi: true,
  },
  {
    provide: ExampleService,
    useExisting: BarService,
    multi: true,
  }
);

This enables you to inject all instances of ExampleService using multi-injection:

ts
import { 
container
} from "./container";
import {
ExampleService
,
FooService
,
BarService
} from "./example.service";
const
fooService
=
container
.
get
(
FooService
);
const
barService
=
container
.
get
(
BarService
);
// Will be the same instances as "fooService" and "barService': const
myServices
=
container
.
get
(
ExampleService
, {
multi
: true });

This even works with multiple levels of inheritance.

What about interfaces? ​

If you're using TypeScript interfaces instead, you should use injection tokens instead. This is because TypeScript interfaces don't exist at runtime and therefore cannot be used as tokens.

ts
import { 
Container
,
InjectionToken
} from "@needle-di/core";
interface Logger {
info
(): void;
} class
FileLogger
implements Logger {
info
(): void {
// } } class
ConsoleLogger
implements Logger {
info
(): void {
// } } const
LOGGER
= new
InjectionToken
<Logger>('LOGGER');
const
container
= new
Container
();
container
.
bindAll
(
{
provide
:
LOGGER
,
multi
: true,
useClass
:
FileLogger
,
}, {
provide
:
LOGGER
,
multi
: true,
useClass
:
ConsoleLogger
,
}, ); const
loggers
=
container
.
get
(
LOGGER
, {
multi
: true });

Released under the MIT License