Reputation: 763
I have a function, that calls render function for different shapes, based on passed params and adding it's name to objects names collection:
export const addRenderObject = ({ type, name, data }: newRenderObject) => {
switch(type) {
case "box": {
renderBox(data);
break;
};
case "line": {
renderLine(data);
break;
};
// etc. shapes
default: throw "unknown collision object type - ${type}";
}
objectsNamesCll.add(name);
};
Here is render function example:
const renderBox = ({ vector, height, width }: newBoxData) => {
// box rendering
};
I describe params types like this:
type newBoxData = {
vector: Vector;
height: number;
width: number;
}
type newCollisionObjectData = {
type: "box";
name: string;
data: newBoxData;
} | {
type: "line";
name: string;
data: newLineData;
};
If i call addRenderObject function with param type field as "box", i expect data field to be newBoxData type, for "line" type - data should be newLineData and so on.
The problem is - it's not cool to rewrite newCollisionObjectData type every time i want to add new shape. But i want to set dependency between values of type and data fields.
I tried to use generic like this:
type newCollisionObjectData<Type, Data> = {
type: Type;
name: string;
data: Data;
}
It will pass an error in renderBox/renderLine functions "Argument of type 'Data' is not assignable to parameter of type 'newBoxData'".
I could use "as" operator when passing params to renderBox function, but all type checking will become useless.
Is there way to make this working and keep it simple?
Upvotes: 1
Views: 345
Reputation: 518
Just let create collision-object-data.model.ts
, box.model.ts
and line.model.ts
:
export abstract class CollisionObjectData<TData extends CollisionObjInnerData> implements CollisionObjectData {
data: TData;
abstract render(): void;
}
export interface CollisionObjectData {
render(): void;
}
export class Box extends CollisionObjectData<BoxData> {
constructor(data: BoxData) {
this.data = data;
}
render(): void {
// rendering logic, using this.data...
}
}
When it comes to BoxData
, it looks like that:
export class BoxData implements CollisionObjInnerData {
name: string = Box.name;
// rest of properties...
}
export interface CollisionObjInnerData {
name: string;
}
Now your addRenderObject(...)
will be look like that:
export const addRenderObject = (data: CollisionObjInnerData) => {
const collisionObjFactory = new CollisionObjDataFactory();
const collisionObj: CollisionObjectData = collisionObjFactory.create(data);
collisionObj.render();
// objectsNamesCll.add(collisionObj.data.name);
};
When it comes to CollisionObjDataFactory
:
export class CollisionObjDataFactory {
create(data: CollisionObjInnerData): CollisionObjectData {
switch(data.name) {
case "box":
return new Box(data);
case "line":
return new Line(data);
}
}
}
Upvotes: 1