Consider something like this:
type BodyTypeA = {
width: number;
height: number;
label: string;
id: string;
importantNumber: number;
// Many other BodyTypeA specific fields
}
type BodyTypeB = {
width: number;
height: number;
}
type BodyTypeC = {
width: number;
height: number;
}
class PhysicsObject {
body: BodyTypeA | BodyTypeB | BodyTypeC
createFromSprite(...) {
this.body = // This is what I use and it will create a BodyTypeA body
}
createFromJson() {
this.body = // This will set body to BodyTypeB body
}
createFromWhatever() {
this.body = // This will set body to BodyTypeC body
}
}
I wrote that on a notepad, so there could be some issues.
My problem is that I have a situation where I have objects which are instances of PhysicsObject
. This PhysicsObject
class and its typings are owned by a third-party library and I cannot / don't want to edit them. Every PhysicsObject
has a body
-field with three possible BodyTypes
- I don't know why, but that's just how it is.
I am basically only creating bodies
for PhysicsObjects
with the createFromSprite
-method, which creates a BodyTypeA
body for the PhysicsObject
.
The issue here is that when I create a body and then want to set id
, label
and many other fields for the body, I have to do it by constantly asserting that the physicsObject.body
is of type BodyTypeA
:
let physicsObject = new PhysicsObject();
physicsObject.createFromSprite(...);
(physicsObject.body as BodyTypeA).label = "abc";
(physicsObject.body as BodyTypeA).id = 124;
let saveImportantNumber = (physicsObject.body as BodyTypeA).importantNumber;
...
So I am wondering if I could somehow persist the type assertion here, without touching any of the third-party library code or types?
I am looking for something like this:
let physicsObject = new PhysicsObject();
physicsObject.createFromSprite(...);
// Assertion block?
(physicsObject.body as BodyTypeA) {
// No need to spam type assertion
physicsObject.body.label = "abc";
physicsObject.body.id = 124;
let saveImportantNumber = physicsObject.body.importantNumber;
...
}
// Just assert it and persist somehow?
physicsObject.body as BodyTypeA;
// No need to spam type assertion
physicsObject.body.label = "abc";
physicsObject.body.id = 124;
let saveImportantNumber = physicsObject.body.importantNumber;
...
Or is there some other way to do something like this that I'm not aware of?
For whatever reason when creating the body
with createFromSprite
, the type of the body
doesn't like "collapse" to BodyTypeA
. I am not sure if this is something that TypeScript should do, but if so, it doesn't happen here. I believe that createFromSprite
doesn't exactly create a BodyTypeA
body, but instead wants to keep it general enough. However, fields like id
and label
definitely don't exist on PhysicsObject.body
created with other methods than CreateFromSprite
.
CodePudding user response:
You can persist the type assertion of a variable, but not of an object property (since that might get mutated in the meantime).
const body = physicsObject.body as BodyTypeA;
body.label = "abc";
body.id = 124;
let saveImportantNumber = body.importantNumber;
CodePudding user response:
One solution would be to just assign the body to its own variable and type that like this:
const body = physicsObject.body as BodyTypeA;
body.label = "abc";
body.id = 124;
Another way would be to use a type predicate, this however does require your createFromSprite
to set the id
, but it is a safer way to do things:
if (isBodyTypeA(physicsObject.body)) {
physicsObject.body.label = "abc";
physicsObject.body.id = 124;
}
function isBodyTypeA(body: BodyTypeA | BodyTypeB | BodyTypeC): body is BodyTypeA {
return body.hasOwnProperty('id');
}