Open justinmchase opened 3 years ago
Thank you for the suggstion, @justinmchase! As far as I can tell, this feature wouldn't be trivial to implement. At the very least it would require adding injection tokens, as I cannot get the generic array's item types out of TypeScript's reflection system. If I'm already adding tokens, I should probably also add a way to inject non-class types (i.e. arbitrary values) using these tokens. I'll need to give this some thought to find an API I'm happy with.
I haven't used typedi myself, but from looking at the documentation I don't understand why the multiple: true
is defined on the @Service
decorator instead of the token itself. What would happen when I decorate two classes with @Service
, one with multiple: true
and the other with multiple: false
and then try to inject those?
Without having spent too much time thinking about this, the following approach might work:
InjectionToken
: for injecting a single class (i.e. what @Injectable()
does now)MultiInjectionToken
: covers your use-case of injecting multiple classes implementing the same interface/extending the same parent class (@Injectable({ token: MultiInjectionToken })
)ValueInjectionToken
: allows you to inject a single value (paired with functions to register them with the injector)MultiValueInjectionToken
: allows you to inject multiple values at onceIInjectionToken
interface@Inject({ token: IInjectionToken })
(or @Inject(token: IInjectionToken)
) decorator that you can apply to constructor parameters that aren't simply an @Injectable
classSo for your use-case it might look something like this:
interface IExample {
name: string;
}
const IExampleToken = new MultiInjectionToken<IExample>("IExample");
@Injectable({ token: IExampleToken })
class A implements IExample {
public name = "A";
}
@Injectable({ token: IExampleToken })
class B implements IExample {
public name = "B";
}
@Bootstrapped()
class Main {
constructor(@Inject({ token: IExampleToken }) private readonly services: IExample[]) {
console.log(services.map(s => s.name)); // [ "A", "B" ]
}
}
What do you think?
Those all seem perfectly reasonable, I can't think of a reason why isMulti
is on the service either. I can't remember what it does if you don't specify multi, it either throws on the second addition or overwrites... both of which really don't make sense. Also its unclear what happens when you call Container.get(token)
for a multi service (the first one? the last one? throw?) or if you have some with multi and some without...
So yeah I think I agree with you that it would make more sense to attach it to the token so that its always multi or not multi and have the one single resolve function return either a single instance or multiple based on the token... yeah seems way better that way.
I need to be able to register and get all services exported as a certain type. Most other DI frameworks have a feature where you put the extra info onto the Injectable attribute.
For example typedi looks like this: