My application displays data about different kinds of business objects in the same standard component.
That is, let's assume there is an Angular component type EditorComponent<T extends object>.
This component is instantiated using a component outlet that receives a custom injector initialized with providers that are specified to each business object type in an interface like this:
interface IBusinessObjectInfo {
readonly providers?: StaticProvider[]
}
The concrete business object types are supplied by individual business modules (that the framework must not know about). Thus, for each business object type, something like this is done:
boInfo: IBusinessObjectInfo = createBusinessObjectInfo({
objectRestrictions: { ... },
...
});
where createBusinessObjectInfo accepts an interface like this:
interface ITypedBusinessObjectInfo<T extends object> {
readonly objectRestrictions: IObjectRestrictions<T>;
...
}
That is, the object passed to createBusinessObjectInfo is generically typed to the business object type.
Now, it's the responsibility of the createBusinessObjectInfo function to generate providers based on the supplied object info that are equipped with appropriately typed injection tokens.
Within EditorComponent<T>, then, I want to inject those objects, and also instantiate some other classes from the framework that work on business objects and are generically typed to them. For example:
@Component...
class EditorComponent<T extends object> {
public constructor() {
this.restrictionViewModel = new RestrictionViewModel<T>(this.objectRestrictions);
}
public readonly restrictionViewModel: RestrictionViewModel<T>;
private readonly objectRestrictions: IObjectRestrictions<T> = inject(...);
}
So far, so good.
The question is: Where/how do I get my generically typed injection tokens from?
I could define them without any typing:
export const objectRestrictionToken = new InjectionToken('object restrictions');
Then, I could cast them upon use, or - slightly more convenient to write - hand them out only using a function:
export function getObjectRestrictionToken<T extends object>(): InjectionToken<IObjectRestrictions<T>> {
return <InjectionToken<IObjectRestrictions<T>>>objectRestrictionToken;
}
Obviously, this solution with an explicit typecast of an existing object instance is not so nice. Nor is it nice that I have to explicitly specify T when calling the getObjectRestrictionToken<T> function.
Is there any other way, without damaging the separation between (generic) framework and concrete business modules?
export interface Type<T> extends Function {new(...args: any[]): T;} export const Type = Function;. We could imagineclass NgComponentOutlet<T = any> {@Input() ngComponentOutlet: Type<T>; @Input() injector: GenInjector<T>; }but alas it's not providedcreateBusinessObjectInfois consistently typed to the entity type. (Obviously, one could abuse that and create one's ownIBusinessObjectInfo, but this will very likely become apparent immediately as the wrong stuff is in the providers and it should be rejected during code review.