I have a problem with generics in TypeScript.
I have an interface called Obstacle
:
import Shape from "./Shape";
interface Obstacle<S extends Shape> {
readonly type: string;
readonly shape: S;
}
export default Obstacle;
Obstacle
is a wrapper around another interface Shape
:
interface Shape {
readonly name: string;
}
export default Shape;
I can use these two interfaces, to create a class that implements Shape
, for example Rectancle
or Circle
.
Then, I create another class, for example RectangleObstacle
or CircleObstacle
which implements Obstacle<Rectangle>
or Obstacle<Circle>
, respectively.
But, my question is, when I use them, in an array of obstacles (it should hold any type of obstacle), like this:
import Obstacle from "./Obstacle";
interface Data {
obstacles: Obstacle<any>[]; /* What should I put here? In the generic parameters list? */
}
const DATA: Data = {
obstacles: []
};
I have tried putting Obstacle<Shape>[]
and Obstacle<any extends Shape>[]
, but it doesn't work. Also, I should be able to distinguish between different types of obstacles, like so:
function somefunc(): void {
for(let i: number = 0; i < DATA.obstacles.length; i++) {
const o: Obstacle = DATA.obstacles[i]; /* what to write here? */
switch(o.type) {
case "rectangle":
/* For example, writting this: */
o.shape.width;
/* results in: property width does not exist on type Shape */
break;
case "circle":
/* ... */
break;
}
}
}
You need to use a discriminated union
type AppObstacle = RectangleObstacle | CircleObstacle
interface Data {
obstacles: AppObstacle[];
}
Also, to apply suggestion by AlekseyL., if obstacle is only a container for shape with no additional data or logic associated with it, you may change Shape to discriminated union as follows:
interface Shape {
readonly type: string;
readonly name: string;
}
interface Rectangle extends Shape {
type: 'rectangle';
width: number;
}
interface Circle extends Shape {
type: 'circle';
radius: number
}
type AppShape = Rectangle | Circle;
interface Data {
obstacles: Obstacle[];
}
const DATA: Data = {
obstacles: []
};
function somefunc(): void {
for(let i: number = 0; i < DATA.obstacles.length; i++) {
const o = DATA.obstacles[i];
const shape = o.shape;
switch(shape.type) {
case 'rectangle':
shape.width;
break;
case 'circle':
shape.radius;
break;
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.