Is it possible to achieve the necessary dynamic typing in TypeScript to establish a component system of this nature?
let block = new Entity();
// block.components === {};
block.has(new Position(0, 0));
// block.components === { position: { ... } }
In this scenario, Entity#components
does not feature an index signature, but rather a specific structure where keys correspond to appropriate component types.
Here is a preliminary attempt at implementing this concept:
class Position implements Component {
name = "position";
x = 0;
y = 0;
}
interface Component {
name: string;
[key: string]: any;
}
class Entity<T={}> {
components: T;
has(component: Component) {
type ExistingComponents = typeof this.components;
type NewComponents = { [component.name]: Component };
this.components[component.name] = component;
return this as Entity<ExistingComponents & NewComponents>;
}
}
There are some issues with this implementation:
- The
has
method updates the original entity with a revised type instead of directly modifying the existing type. - The
NewComponents
type encounters compilation errors due to its dependence on a runtime property (component.name
).
An alternative approach considered involves integrating extension into the components themselves to ensure static names:
class Position implements Component {
name = "position";
x = 0;
y = 0;
static addTo(entity: Entity) {
type Components = typeof entity.components & { position: Position };
entity.components.position = new Position();
return entity as Entity<Components>;
}
}
let position = new Position(0, 0);
position.addTo(block);
However, this method seems cumbersome and still fails to address the challenge of redefining the type without creating a new one.
Is there a way to modify the type during compile time using a method call?