i get weird behavior here,
- if i assign a default array in generic of
Compositor<CT extends ComponentType[]=[]> - ts keep infer
ComponentType[]instead of[]to my typeSystemWithRule!
this make not sense for me !? any help are welcome
- i expected
this.rulesto be infered like this :[typeof ComponentA] - and not like this :
[ComponentType<Component> | typeof ComponentA]
What i miss ? thanks for your time!
type Constructor<T> = new ( ...args: never ) => T;
type ComponentType<T extends Component=Component> = Constructor<T>
abstract class Component {
declare _: never;
declare constructorType: ComponentType<this>;
}
export type SystemWithRule<T, C extends ComponentType[]> = T extends Constructor<System<infer CC>>
? [ ...CC, ...C ]
: never;
abstract class System<R extends ComponentType[]=[]> {
static behavior( behaviors?:'update'|'dirty'|'input'|'none' ) {
return class Compositor<CT extends ComponentType[]=[]> extends System<CT> {
static hasAny<C extends ComponentType[], T>( this:T, components:C ) {
return class extends Compositor< SystemWithRule<T, C> > {
static override hasAny:never;
};
}
static hasAll<C extends ComponentType[], T>( this:T, components:C ) {
return class extends Compositor< SystemWithRule<T, C> > {
static override hasAll:never;
};
}
static hasMaybe<C extends ComponentType[], T>( this:T, components:C ) {
return class extends Compositor< SystemWithRule<T, C> > {
static override hasMaybe:never;
};
}
};
}
declare rules: R;
}
class ComponentA extends Component {
declare private __: never;
}
class ComponentB extends Component {
declare private __: never;
}
// composing systems class with custom rules: ts will AUTO infer composed rules to entities in the systems (no need duplicate interface/inplemebtation)
export class SystemA extends System
.behavior( 'update' )
.hasAny([
ComponentA,
])
// .hasAll([
// ComponentB,
// ])
{
update() {
// entity should: should [typeof ComponentA | typeof ComponentB] and not [ComponentType<Component> | typeof ComponentA | typeof ComponentB] !
// i cant found where the [ComponentType<Component>] was infer!?, because default generic was empty array [] !!!?
this.rules; //
}
}
V2 SIMPLIFIED: tsplayground
I'm not sure I follow the need for such complex recursive generic classes with static fields, but I'll consider that outside the scope of the question.
Your problem seems to be that you except
System.behavior().hasAny()to infer the default generic type argument[]forCompositor, but you're getting the constraintComponentType[]instead. This is working as intended, as per microsoft/TypeScript#16229. Generic defaults don't play a role in inference. If you need to infer something where a generic type parameter has to be instantiated with an argument to proceed, it will end up being erased by widening to the constraint. So for a static member of aCompositor, theCTtype argument will be widened toComponentType[].I'm not sure the full extent of what you're doing, but if I wanted behavior like this I'd try to leverage instantiation expressions so that you can instantiate the type argument explicitly. Maybe like this:
Here the
Compositorclass that's returned will automatically have its generic type argument resolved to[]. Then subsequent calls should be properly generic as expected.Oh, I also needed to give your
hasXXX()methods aconsttype parameter so that when you call them with an array literal like[ComponentA], the compiler interprets this as a tuple type, so that your variadic tuple type concatenations work as you expect. This is mostly off-topic, but it's an issue you would run into after solving the instantiation issue, and I didn't want to have the question digress too much.Let's test it:
Looks good. The type of
rulesis[typeof ComponentA], as you expect.Playground link to code