Service registration
Tiny Injector provide similar interface to .Net DI with almost identical behavior. There are several service registration methods that are useful in different scenarios.
- Using
Injector
- Using
@Injectable
decorator.
Using Lifetime methods#
Single registration#
Syntax | Example |
---|---|
Injector.Add{LIFETIME}({IMPLEMENTATION}) | Injector.AddSingleton(ConcreteDep) |
Injector.Add{LIFETIME}({SERVICE}, {IMPLEMENTATION}) | Injector.AddSingleton(AbstractDep, ConcreteDep) |
Injector.Add{LIFETIME}({SERVICE}, (context) => new {IMPLEMENTATION} ) | Injector.AddSingleton(AbstractDep, (context) => new ConcreteDep()) |
Injector.Add{LIFETIME}({InjectionToken}, (context) => {IMPLEMENTATION}) | Injector.AddSingleton(new InjectionToken<{number}>('A Token'), (context) => 10) |
Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type.
Injector.AddSingleton(ServiceType);
Injector.AddSingleton(ServiceType, ServiceType);
Calling
Add{Lifetime}
more than one time will result in throwingServiceExistException
, if your intentions to register multiple implementations, check outAppend{lifetime}
instead.
Multiple registrations#
Syntax | Example |
---|---|
Injector.Append{LIFETIME}({IMPLEMENTATION}) | Injector.AppendSingleton(ConcreteDep) |
Injector.Append{LIFETIME}({SERVICE}, {IMPLEMENTATION}) | Injector.AppendSingleton(AbstractDep, ConcreteDep) |
Injector.Append{LIFETIME}({SERVICE}, (context) => new {IMPLEMENTATION}) | Injector.AppendSingleton(AbstractDep, (context) => new ConcreteDep()) |
Injector.Append{LIFETIME}({InjectionToken}, (context) => {IMPLEMENTATION}) | Injector.AppendSingleton(new InjectionToken<{number}>('A Token'), (context) => 10) |
Injector.AppendSingleton(ServiceType);
Injector.AppendSingleton(ServiceType, ServiceType);
Injector.AppendSingleton(ServiceType, ImplementationType);
Injector.AppendSingleton(ServiceType, implementationFactory);
Example
abstract class Logger {}
@Injectable()
class InformativeLogger extends Logger {}
@Injectable()
class WarningLogger extends Logger {}
Injector.AppendSingleton(Logger, InformativeLogger);
Injector.AppendSingleton(Logger, WarningLogger);
You can use AddSingleton
for the first add, but it is recommended to use AppendSingleton
if you want array of implementations.
Multiple implementations of a service cannot be registered using the methods that don't take an explicit service type. These methods can register multiple instances of a service, but they will all have the same implementation type.
Injector.AppendSingleton(ServiceType);
Injector.AppendSingleton(ServiceType);
Injector.AppendSingleton(ServiceType);
const services = Injector.GetServices(ServiceType);
// services[0], services[1], and services[2] will have the same type as ServiceType
// Basically, it is like creating 3 instances of ServiceType
Calling
Injector.GetRequiredService(ServiceType)
will return the last registered implementation
Safe registration#
Syntax | Example |
---|---|
Injector.TryAdd{LIFETIME}({IMPLEMENTATION}) | Injector.TryAddSingleton(ConcreteDep) |
Injector.TryAdd{LIFETIME}({SERVICE}, {IMPLEMENTATION}) | Injector.TryAddSingleton(AbstractDep, ConcreteDep) |
Injector.TryAdd{LIFETIME}({SERVICE}, (context) => new {IMPLEMENTATION}) | Injector.TryAddSingleton(AbstractDep, (context) => new ConcreteDep()) |
Injector.TryAdd{LIFETIME}({InjectionToken}, (context) => {IMPLEMENTATION}) | Injector.TryAddSingleton(new InjectionToken<{number}>('A Token'), (context) => 10) |
Register the service only if there isn't already an implementation registered.
Injector.TryAdd(ServiceType, FirstImplementationType);
Injector.TryAdd(ServiceType, SecondImplementationType);
Only FirstImplementationType
will be registered.
Example#
@Injectable()
abstract class Logger {}
@Injectable()
class InformativeLogger extends Logger {}
Injector.AddSingleton(Logger, InformativeLogger);
Using factory as implementation
Injector.AddSingleton(Logger, (context) => {
// Some interesting logic
return new InformativeLogger();
});
Make sure that ServiceType is always class syntax otherwise an error will be thrown. @Injectable is must present on the service class otherwise it won't be resolved
Using @Injectable#
The other approche is to use @Injectable
decorator as it is the best choice for tree shaking purposes.
with @Injectable
you don't need to explict service registration using Add{Lifetime}
methods.
In some cases you might need to use litetime methods such as, deffered registration or if you want to provide factoryFn as implementation
Example#
Register ServiceType
as Singleton,
@Injectable({
lifetime: ServiceLifetime.Singleton,
})
class ServiceType {}
Remember, registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. so this would be registered as this behind the scene
@Injectable()
class ServiceType {}
Injector.AddSingleton(ServiceType, ServiceType);
Register InformativeLogger
as Singleton against Logger
abstract class Logger {}
@Injectable({
lifetime: ServiceLifetime.Singleton,
serviceType: Logger,
})
class InformativeLogger extends Logger {}
This is an alias for
Injector.AddSingleton(Logger, InformativeLogger);
keep in mind that
@Injectable
is always needed in order to resolve the class constructor parameters type.
Strict Type#
Thanks to TypeScript, the implementation will be constrained to the ServiceType.
e.g
@Injectable()
class Vehicle {
start() {}
}
@Injectable()
class Weapon {
shoot() {}
}
Injector.AddSingleton(Vehicle, Weapon);
Injector.AddSingleton(Vehicle, (context) => new Weapon());
the above sample won't work since Weapon
is not subtype of Vehicle
@Injectable()
class Vehicle {
start() {}
}
@Injectable()
class Truck extends Vehicle {
warmUp() {}
}
Injector.AddSingleton(Vehicle, Truck);
Injector.AddSingleton(Vehicle, (context) => new Truck());
now the implementation either factory or type will work just fine since the Truck is subtype of Vehicle.
Type Equivalence#
Because of JavaScript nature, having different classes with the same properties and methods make them equivalent in regards to type,
so the below code will work with no errors, although, this code can be handled at runtime using instanceof
keyword
class Test {
variable = 10;
}
class Service {
variable = 10;
}
class Implementation extends Test {}
Injector.AddSingleton(Service, Implementation);