When using a mixin that builds up on a generic class, I need to set it's value to unknown, causing the implementing class having a generic parameter of type unknown | concrete
.
I've build an example here using Angular, but it's completely Typescript related: https://stackblitz.com/edit/angular-svw6ke?file=src%2Fapp%2Fapp.component.ts
Is there any chance to redesign this mixin (using Typescript 4.4) so the type won't get malformed?
You need to carry the generic parameter through the mixin function. Titian's answer here gives a good explanation of how this works. For your problem, it would look something like
export function genericMixin1<
ModelType,
ComponentType extends AbstractConstructor<ComponentBase<ModelType>>
>(Base: T) {
abstract class GenericMixin extends Base {
//Logic here
}
return GenericMixin;
}
It helps to know going in that what he calls the "two call approach" stems from a workaround for a common problem with TypeScript generics -- you can't partially infer generic type parameters when calling a function, but you can sort of "divide" the generic parameters between the one you're calling and a second inner function. For example:
export function genericMixin2<T>() {
return function<
ComponentType extends AbstractConstructor<ComponentBase<T>>
>(Base: ComponentType) {
abstract class GenericMixin extends Base {
//Logic here
}
return GenericMixin;
};
}
(ETA: Fixed, Playground example here.)
Now instead of writing const StringMixin = genericMixin1<string, ComponentBase<string>>(ComponentBase);
you can write const StringMixin = (genericMixin2<string>()(ComponentBase))
. This is only a slight improvement with one generic parameter, but becomes absolutely necessary when you have many generic params. Otherwise, you wind up with const ManyParamsMixin = genericMixin<string, number, boolean, BaseThing<string,number,boolean>>(BaseThing)
... it gets ugly fast.