EDIT: reproducible link to typescript's playground
I also found a solution that is in the link as well, though I don't actually understand why my first one doesn't work if anyone still wants to educate me.
TLDR;
this does not work
getEntitiesByComponent<T extends Component<any>>(compType: new (...args: any[]) => T | Array<new (...args: any[]) => T>): IEntity[] {
this works
getEntitiesByComponent<T extends new (...args: any[]) => Component<any>>(compType: T | Array<T>): IEntity[]
EDIT 2
Code from reproducible link
export interface IComponent {
setEntity: (value: IEntity) => void;
getEntity: () => IEntity;
}
export interface IEntity {
components: any[];
}
export const entitySet = new Set<IEntity>();
export class Component<T extends object> implements IComponent {
private _entity: IEntity | undefined;
setEntity(value: IEntity) {
if (this._entity === value) {
return;
}
this._entity = value;
}
getEntity(): IEntity {
return this._entity!;
}
props: T;
constructor(props?: T) {
this.props = {
...props as T
}
}
}
export class ViewComponent extends Component<any> {
constructor(props: any) {
super(props);
}
}
export class TransformComponent extends Component<any> {
constructor(props?: any) {
super();
}
}
// THIS TYPING DOESN'T WORK
function getEntitiesByComponent<T extends Component<any>>(compType: new (...args: any[]) => T | Array<new (...args: any[]) => T>): IEntity[] {
const compTypes = Array.isArray(compType) ? compType : [compType];
return Array.from(entitySet.values())
.filter(e => {
return e.components.some(c => compTypes.some(compType => c instanceof compType))
});
}
// THIS TYPING DOES WORK
function getEntitiesByComponentThatWorks<T extends new (...args: any[]) => Component<any>>(compType: T | Array<T>): IEntity[] {
const compTypes = Array.isArray(compType) ? compType : [compType];
return Array.from(entitySet.values())
.filter(e => e.components.some(c => compTypes.some(compType => c instanceof compType)))
}
// error
const viewComponentEntities = getEntitiesByComponent([ViewComponent, TransformComponent]);
// no error
const viewComponentEntitiesWorks = getEntitiesByComponentThatWorks([ViewComponent, TransformComponent]);
ORIGINAL
I'm trying to define a function that accepts single instance of- or an array of instances of objects that subclass a single parent class.
// EntityManager.ts
/**
* this func should receive either a single instance of a constructor that returns a
* Component<any> or an array of the same and should return only entities that have a
* component that is an instance of one of them.
**/
getEntitiesByComponent<T extends Component<any>>(compType: new (...args: any[]) => T | Array<new (...args: any[]) => T>): IEntity[] {
const compTypes = Array.isArray(compType) ? compType : [compType];
return Array.from(this._entitySet.values())
.filter(e => {
return e.components.some(c => compTypes.some(compType => c instanceof compType))
});
}
ViewComponent and TransformComponent both extend Component.
// ViewComponent.ts
import { Component } from '../components/component';
export type ViewComponentProps = {
alias: string;
src: string;
}
export class ViewComponent extends Component<ViewComponentProps> {
constructor(props: ViewComponentProps) {
super(props);
}
}
// TransformComponent.ts
import { Component } from './component';
export type TransformComponentProps = {
x: number;
y: number;
scaleX?: number;
scaleY?: number;
zIndex?: number;
}
export class TransformComponent extends Component<TransformComponentProps> {
private static _defaultProps: TransformComponentProps = {
x: 0,
y: 0,
scaleX: 1,
scaleY: 1,
zIndex: 0,
};
constructor(props?: TransformComponentProps) {
super({
...TransformComponent._defaultProps,
...props
});
}
}
When I invoke it like so
const viewComponentEntities = EntityManager.getInstance().getEntitiesByComponent([ViewComponent, TransformComponent]);
Typescript gives me this error
TS2345: Argument of type
(typeof TransformComponent | typeof ViewComponent)[]
is not assignable to parameter of type
new (...args: any[]) => Component<any> | (new (...args: any[]) => Component<any>)[]
Type
(typeof TransformComponent | typeof ViewComponent)[]
provides no match for the signature
new (...args: any[]): Component<any> | (new (...args: any[]) => Component<any>)[]
I'm not understanding why I'm getting the error or how to prevent it.
With @jcalz help, it was pointed out I simply had a syntax error.
getEntitiesByComponent<T extends Component<any>>(compType: new (...args: any[]) => T | Array<new (...args: any[]) => T>): IEntity[]
My type was looking for a constructor that returned T
or Array<new (...args: any[]) => T>
rather than a single constructor (new (...args: any[]) => T)
or array of them Array<new (...args: any[]) => T>
so this now works
getEntitiesByComponent<T extends Component<any>>(compType: (new (...args: any[]) => T) | Array<new (...args: any[]) => T>): IEntity[]